<?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: Deepjyoti Barman</title>
    <description>The latest articles on DEV Community by Deepjyoti Barman (@deepjyoti30).</description>
    <link>https://dev.to/deepjyoti30</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%2F185022%2F7b84bc57-ac26-4005-9c85-81b87246845b.jpg</url>
      <title>DEV Community: Deepjyoti Barman</title>
      <link>https://dev.to/deepjyoti30</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deepjyoti30"/>
    <language>en</language>
    <item>
      <title>Dynamic Path Matching with Go and mux</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Sun, 16 Jul 2023 13:29:57 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/dynamic-path-matching-with-go-and-mux-2m9n</link>
      <guid>https://dev.to/deepjyoti30/dynamic-path-matching-with-go-and-mux-2m9n</guid>
      <description>&lt;p&gt;Go is by far one of the most interesting languages that I have worked with and I have enjoyed working with it almost everytime (except when functions do not support default values). Recently, I came across this problem where I had a use-case to support user defined routes for an API. Problem was supporting these user defined URL's in real time and actually triggerring a handler based on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  More details of the problem
&lt;/h2&gt;

&lt;p&gt;The use-case I had was that the users will be allowed to define any custom route path and some handler logics that should be executed based on that. It's easy to handle the part of triggering the user defined logic based on the route matched. Hard part is to check whether an incoming request is supposed to be matched against an user-defined route or a system route.&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%2Fraw.githubusercontent.com%2Fdeepjyoti30%2Fblog-static-content%2Fmaster%2Fcontent%2Fdynamic-url-matching.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%2Fraw.githubusercontent.com%2Fdeepjyoti30%2Fblog-static-content%2Fmaster%2Fcontent%2Fdynamic-url-matching.png" alt="Dynamic URL Matching Problem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Naive Approach that was considered
&lt;/h2&gt;

&lt;p&gt;I considered this naive approach where I would restart the router (using Mux) everytime an user defined route is added/deleted/updated and the router will know whether the incoming request is for an user defined route or system.&lt;/p&gt;

&lt;p&gt;This approach is simple enough but is a very bad practice. Imagine thousands of users updating their routes every now and then and the whole router restarts and the API (as a whole) goes down for a few seconds. This can be a major problem for a production API that is customer facing.&lt;/p&gt;

&lt;p&gt;Thus, there was need for figuring out an alternate approach to this problem that would be efficient as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's Solved
&lt;/h2&gt;

&lt;p&gt;Just a heads up, before I dive into details, the problem was solved by using &lt;code&gt;mux&lt;/code&gt; and it's useful functions (thanks to the developers for considering a scenario like this while developing).&lt;/p&gt;

&lt;p&gt;In order to understand the solution to the problem, we will have to understand how &lt;code&gt;mux&lt;/code&gt; handles an incoming request. The flow is important here in order to solve this problem in a very efficient way.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;mux&lt;/code&gt;, every route has two properties (or functions) that can be defined against it. One of them is a &lt;code&gt;matcher&lt;/code&gt; which can be defined optionally but not necessarily required. The other is a &lt;code&gt;handler&lt;/code&gt; which is the actual function that will be trigerred if the route is matched.&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%2Fraw.githubusercontent.com%2Fdeepjyoti30%2Fblog-static-content%2Fmaster%2Fcontent%2Fmux-route-matching-flow.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%2Fraw.githubusercontent.com%2Fdeepjyoti30%2Fblog-static-content%2Fmaster%2Fcontent%2Fmux-route-matching-flow.png" alt="Mux Route Matching Flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Matchers
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;mux&lt;/code&gt; uses matchers to determine whether a route should match. &lt;code&gt;matchers&lt;/code&gt; are just mere functions which return a boolean value. This value indicates whether the route is matched or not matched.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;matcher&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;: route is matched and handler should be called.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;matcher&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;: route is not matched&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: One thing to know here is that &lt;code&gt;mux&lt;/code&gt; goes through each route that is defined in the router until a &lt;code&gt;matcher&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; or the end of routes is reached. When end of routes is reached, a 404 is returned with an error indicating the page is not found.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Matchers are a boon in this particular scenario as this makes solving dynamic user defined routes really simple.&lt;/p&gt;

&lt;p&gt;Here's an example matcher that matches the incoming route if it is &lt;code&gt;/hello&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getHelloMatcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MatcherFunc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rm&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RouteMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"/hello"&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;Above is a very simple example of how a custom matcher can be defined but in order to match against a dynamic route, it is better to use &lt;code&gt;mux&lt;/code&gt;'s own functions. &lt;code&gt;mux&lt;/code&gt; internally uses a &lt;code&gt;Match&lt;/code&gt; function that checks if a route matches the incoming requests path and accordingly returns &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. It is ideal for us to use this function as they use some complex regex to match the path.&lt;/p&gt;

&lt;p&gt;Following code shows how to use &lt;code&gt;mux&lt;/code&gt;'s internal &lt;code&gt;Match&lt;/code&gt; function:&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getHelloMatcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MatcherFunc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rm&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RouteMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// In order to use a internal `mux` function we will need to&lt;/span&gt;
        &lt;span class="c"&gt;// define a dummy router that can provide the function.&lt;/span&gt;
        &lt;span class="n"&gt;copyMuxRouter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StrictSlash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;copyMuxRouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dynamic router"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                &lt;span class="n"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;As explained, above code uses an internal function to match the incoming route against a route &lt;code&gt;/hello&lt;/code&gt;. This part of the code can be made dynamic based on user defined route that can be fetched accordigly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handlers
&lt;/h3&gt;

&lt;p&gt;After a &lt;code&gt;matcher&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;, the route's &lt;code&gt;handler&lt;/code&gt; is called. In &lt;code&gt;mux&lt;/code&gt;, all routes will absolutely need a &lt;code&gt;handler&lt;/code&gt; defined against it or else &lt;code&gt;mux&lt;/code&gt; will not start the router and throw an error at startup.&lt;/p&gt;

&lt;p&gt;Pretty much anyone who has used &lt;code&gt;mux&lt;/code&gt; is aware of how to define a handler so I will not go over the steps on that. &lt;a href="https://github.com/gorilla/mux#full-example" rel="noopener noreferrer"&gt;Here's a full example from mux docs on how to define a handler for a route&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhancements to the above
&lt;/h2&gt;

&lt;p&gt;The problem that I initially mentioned can be solved by using &lt;code&gt;matchers&lt;/code&gt; as I have explained. However, there are certain places where the code can be made efficient. The &lt;code&gt;matcher&lt;/code&gt; function that &lt;code&gt;mux&lt;/code&gt; supports, uses a &lt;code&gt;RouteMatch&lt;/code&gt; type. This is a custom type inside of which there's a &lt;code&gt;Vars&lt;/code&gt; key which maps to a &lt;code&gt;map&lt;/code&gt; type. This &lt;code&gt;Vars&lt;/code&gt; can be used to pass details from the &lt;code&gt;matcher&lt;/code&gt; to the &lt;code&gt;handler&lt;/code&gt; in order to reduce some redundant steps.&lt;/p&gt;

&lt;p&gt;As an example, say the user defined route is stored in a database and the user can also define different methods for the route. These details might come handy in the &lt;code&gt;handler&lt;/code&gt;. In our scenario, these details are definitely required in the &lt;code&gt;matcher&lt;/code&gt; phase of the code where a database call can be made to get the details for a route.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;handler&lt;/code&gt; phase of the flow, instead of making another database call to get the details of the matched route, those details can simply be passed from the &lt;code&gt;matcher&lt;/code&gt; to the &lt;code&gt;handler&lt;/code&gt; by using the &lt;code&gt;Vars&lt;/code&gt; of the &lt;code&gt;RouteMatch&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;I am not sure if &lt;code&gt;RouteMatch.Vars&lt;/code&gt; was intentionally designed to do that but it is definitely a good hack (if you will) to make the code efficient and relatively faster (database network calls are expensive).&lt;/p&gt;

&lt;p&gt;Following example shows how some details are passed from the &lt;code&gt;matcher&lt;/code&gt; to the &lt;code&gt;handler&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getHelloMatcher&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MatcherFunc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rm&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RouteMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// In order to use a internal `mux` function we will need to&lt;/span&gt;
        &lt;span class="c"&gt;// define a dummy router that can provide the function.&lt;/span&gt;
        &lt;span class="n"&gt;copyMuxRouter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StrictSlash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;isMatched&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;copyMuxRouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Methods&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dynamic router"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
                &lt;span class="n"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isMatched&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;rm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"MATCHED_ROUTE_METHODS"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;isMatched&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;Following is how to use the &lt;code&gt;MATCHED_ROUTE_METHODS&lt;/code&gt; in the &lt;code&gt;handler&lt;/code&gt;:&lt;/p&gt;

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

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getHelloHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rw&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Read the vars&lt;/span&gt;
        &lt;span class="n"&gt;vars&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;mux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Vars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// Get the methods.&lt;/span&gt;
        &lt;span class="n"&gt;methods&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"MATCHED_ROUTE_METHODS"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Matched methods are: "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;GoLang is becoming one of my favorite languages to work on and I am very close to even replacing Python with GoLang in my next project. I think everyone should give it a try to find out the beauty of it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gorrila/mux&lt;/code&gt; team has been doing a great job building this awesome router. Go give it a star at &lt;a href="https://github.com/gorilla/mux" rel="noopener noreferrer"&gt;gorilla/mux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.deepjyoti30.dev/dynamic-path-go-mux" rel="noopener noreferrer"&gt;This post was originally posted at my blog at https://blog.deepjyoti30.dev&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>api</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Setup your own blog on top of GitHub</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Sat, 06 Nov 2021 06:07:38 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/setup-your-own-blog-on-top-of-github-2ik3</link>
      <guid>https://dev.to/deepjyoti30/setup-your-own-blog-on-top-of-github-2ik3</guid>
      <description>&lt;p&gt;Have you ever thought of creating your own blog in your own .dev domain that you could just share with anyone and forget?&lt;/p&gt;

&lt;p&gt;Well, it has never been easier to create a blog. I myself have a separate blog on my domain and every now and then I end up redesigning the whole thing. However, the tedious part about maintaining a blog is every time you need to keep on moving your posts from one place to another.&lt;/p&gt;

&lt;p&gt;This is why I came up with Emanates. Emanates is a tool that lets you build a blog &lt;strong&gt;on top of GitHub&lt;/strong&gt; in just a few minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;The idea is simple. There are two key things to know about Emanates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every GitHub Issue is a blog post&lt;/li&gt;
&lt;li&gt;Every time an issue is created/updated/deleted a GitHub Action is run that builds the latest static blog and deploys it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why Emanates?
&lt;/h2&gt;

&lt;p&gt;Now, why should you go with Emanates instead of building your own blog?&lt;/p&gt;

&lt;p&gt;Well, Emanates also comes with some of the latest features that people want in a blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment support&lt;/li&gt;
&lt;li&gt;Built in SEO&lt;/li&gt;
&lt;li&gt;Support for automatic related posts (Emanates does that automatically, you just need to use the proper labels)&lt;/li&gt;
&lt;li&gt;Metadata supported&lt;/li&gt;
&lt;li&gt;Cover image supported&lt;/li&gt;
&lt;li&gt;Dark Mode (obviously)&lt;/li&gt;
&lt;li&gt;Everything is stored on GitHub, no hassles of databases and such.&lt;/li&gt;
&lt;li&gt;It is fast (it's a static site)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Show me how it is
&lt;/h2&gt;

&lt;p&gt;If you want to get a taste of how it looks like when it is live, check out the demo page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://emanates-demo.netlify.app/"&gt;https://emanates-demo.netlify.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some images:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ThWY9dXj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3tjslzsyaowc3a6rf5k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ThWY9dXj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3tjslzsyaowc3a6rf5k.png" alt="Emanates Main page" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UIvjCywh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rahk09s4ily5dk7kes8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UIvjCywh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rahk09s4ily5dk7kes8a.png" alt="Blog Head" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GzgjNU6c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxejw9wukiinentdvr3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GzgjNU6c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vxejw9wukiinentdvr3v.png" alt="Blog content" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How should I start
&lt;/h2&gt;

&lt;p&gt;Get started with Emanates by checking the GitHub repo here: &lt;a href="https://github.com/emanates/emanates-web"&gt;https://github.com/emanates/emanates-web&lt;/a&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>github</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Emanates: Build a blog on top of GitHub Issues and Actions</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Mon, 23 Aug 2021 13:01:21 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/emanates-build-a-blog-on-top-of-github-issues-and-actions-2lcm</link>
      <guid>https://dev.to/deepjyoti30/emanates-build-a-blog-on-top-of-github-issues-and-actions-2lcm</guid>
      <description>&lt;p&gt;I am happy to announce the launch of Emanates. This idea came in my mind way back when GitHub launched GitHub Actions.&lt;/p&gt;

&lt;p&gt;The idea of Emanates is every GitHub Issue is a blog post and when an issue is created, a workflow is triggered (GitHub Action).&lt;/p&gt;

&lt;p&gt;This workflow builds the blog and deploys it to your desired location.&lt;/p&gt;

&lt;p&gt;Emanates boasts of some nice features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved SEO&lt;/li&gt;
&lt;li&gt;Support for related posts (based on tags added through GitHub Labels)&lt;/li&gt;
&lt;li&gt;Meta tag support along with cover image and other things.&lt;/li&gt;
&lt;li&gt;Markdown support&lt;/li&gt;
&lt;li&gt;Supports comments (through utterances)&lt;/li&gt;
&lt;li&gt;Blazingly fast (since it builds a static site)&lt;/li&gt;
&lt;li&gt;Dark Mode (obviously!)&lt;/li&gt;
&lt;li&gt;Everything on GitHub (no need to trust me)&lt;/li&gt;
&lt;li&gt;Open Source&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/emanates/emanates-web"&gt;You can check Emanates GitHub here&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://emanates-demo.netlify.app"&gt;Demo site for anyone interested&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;I'm attaching a few images to show how Emanates looks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yhqd1bZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fi5kmev0t9931b50u7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yhqd1bZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fi5kmev0t9931b50u7j.png" alt="Emanates index page" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yhqd1bZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fi5kmev0t9931b50u7j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yhqd1bZh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7fi5kmev0t9931b50u7j.png" alt="Emanates Post" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>A handy deployment script for your pipeline</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Fri, 16 Jul 2021 12:15:25 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/a-handy-deployment-script-for-your-pipeline-46gk</link>
      <guid>https://dev.to/deepjyoti30/a-handy-deployment-script-for-your-pipeline-46gk</guid>
      <description>&lt;p&gt;Just after GitHub Actions was launched, I was pretty excited to try it out. So I did what any other developer would have done, I wrote an workflow file to deploy my API's automatically to my server.&lt;/p&gt;

&lt;p&gt;After a few tries and catch blocks, ;-), I was able to get it to work properly. Back then, my idea of the pipeline was, the script would ssh into my server, build the dockerfile locally and then deploy it.&lt;/p&gt;

&lt;p&gt;However, after talked with a few people, turns out that's not the correct way to use it. I started reading a few articles and I was indeed wrong in thinking the building process is supposed to be done locally on the host machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how to do it then?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to get a proper idea of how to setup a pipeline, read my &lt;a href="https://blog.deepjyoti30.dev/github-actions-pipeline"&gt;other article on it&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I will just go through the workflow in brief as of now.&lt;/p&gt;

&lt;p&gt;Since the pipeline might deploy the docker image in various containers (which obviously adds the possibility of multiple platforms, also in the case of releases), the building process should be rather done in the pipeline.&lt;/p&gt;

&lt;p&gt;After the building is done, the image should be pushed to a container hub , yep, you guessed it, &lt;strong&gt;Docker Hub&lt;/strong&gt; or in my case &lt;strong&gt;GHCR&lt;/strong&gt; (since it's free). After the image is pushed, the workflow should deploy the image in various platforms.&lt;/p&gt;

&lt;p&gt;I personally do it by using the &lt;a href="https://github.com/appleboy/ssh-action"&gt;ssh-action&lt;/a&gt; package that allows my workflow to ssh into my server(s), pull the latest image from the hub and deploy it.&lt;/p&gt;

&lt;p&gt;Here's where my article comes in place, how do you deploy locally?&lt;/p&gt;

&lt;p&gt;Obviously a simple &lt;code&gt;docker pull&lt;/code&gt; and &lt;code&gt;docker run&lt;/code&gt; won't cut it right? There are ports to be taken care of, volumes mapping, environment files.&lt;/p&gt;

&lt;p&gt;I had the same question and I came across an article (that I am unable to find right now), that had a pretty nice script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment Script
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The script I am sharing here is not mine. It was created by someone else and I merely just edited it a bit to fit my requirements. I am unable to find the article of the original author of the script.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;########################################&lt;/span&gt;
&lt;span class="c"&gt;# Put this on a Server&lt;/span&gt;
&lt;span class="c"&gt;# run chmod +x deploy_app.sh to make the script executable&lt;/span&gt;
&lt;span class="c"&gt;# &lt;/span&gt;
&lt;span class="c"&gt;# Execute this script:  ./deploy_app.sh docker-username/docker-appname:$TAG&lt;/span&gt;
&lt;span class="c"&gt;# Replace the $TAG with the actual Build Tag you want to deploy&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;########################################&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;DOCKER_IMAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;CONTAINER_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"container-name"&lt;/span&gt;

&lt;span class="c"&gt;# Check for arguments&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-lt&lt;/span&gt; 1 &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'[ERROR] You must supply a Docker Image to pull'&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploying &lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt; to Docker Container"&lt;/span&gt;

&lt;span class="c"&gt;#Check for running container &amp;amp; stop it before starting a new one&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker inspect &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.State.Running}}'&lt;/span&gt; &lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;docker stop &lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Pull the latest image&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pulling latest image for &lt;/span&gt;&lt;span class="nv"&gt;$DOCKER_IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

docker pull &lt;span class="nv"&gt;$DOCKER_IMAGE&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting &lt;/span&gt;&lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt;&lt;span class="s2"&gt; using Docker Image name: &lt;/span&gt;&lt;span class="nv"&gt;$DOCKER_IMAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 80:5000 &lt;span class="nt"&gt;--env-file&lt;/span&gt; ~/Scripts/.env &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$CONTAINER_NAME&lt;/span&gt; &lt;span class="nv"&gt;$DOCKER_IMAGE&lt;/span&gt;

&lt;span class="c"&gt;# Show the running processes&lt;/span&gt;
docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above script is basically pulling the latest image and deploying it according to the passed flags like &lt;code&gt;--env-file&lt;/code&gt;. However, this also checks if the container is already running. If so, the container is first removed and after the new container is &lt;strong&gt;run&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can directly copy the script to your server, just do the following changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the &lt;code&gt;CONTAINER_NAME&lt;/code&gt; value.&lt;/li&gt;
&lt;li&gt;Make the script executable using &lt;code&gt;chmod +x &amp;lt;script_path&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't forget to pass the name of the image while running the script, eg: &lt;code&gt;sh script.sh ghcr.io/deepjyoti30/ytmdl-web-v2:latest&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is important to pass the &lt;code&gt;latest&lt;/code&gt; tag or the exact tag because otherwise a cached image of the container is used. This means the image is not updated at all. So make sure the tag is passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example workflow with the script
&lt;/h2&gt;

&lt;p&gt;Once you have the script setup nice and cozy on your server, you can setup a workflow like following to ssh into the server and run the script. We will use &lt;code&gt;ssh-action&lt;/code&gt; as mentioned above to ssh into the server. Once done, that action will just run the script.&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 app and deploy docker&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production"&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;push-to-docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Write actions here to build and push the image&lt;/span&gt;
    &lt;span class="c1"&gt;# to a hub like Docker Hub or Github Container Registry&lt;/span&gt;
    &lt;span class="c1"&gt;# NOTE: Don't leave these lines as such, the workflow will fail.&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;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push-to-docker&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="c1"&gt;# SSH into the server&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;SSH into server&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.SERVER_IP }}&lt;/span&gt; &lt;span class="c1"&gt;# Pass server IP&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.SERVER_USERNAME }}&lt;/span&gt; &lt;span class="c1"&gt;# Pass server username&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.KEY }}&lt;/span&gt; &lt;span class="c1"&gt;# Pass server ssh private key&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/bin/bash Scripts/deploy_ytmdl_web.sh ghcr.io/deepjyoti30/ytmdl-web:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the above workflow in action in &lt;a href="https://github.com/deepjyoti30/ytmdl-web-v2"&gt;my ytmdl-web repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you happen to come accross the article that had the above script originally, feel free to comment. I will update the post with credits (and links) to that!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://blog.deepjyoti30.dev/handy-deployment-script"&gt;This article was originally posted on my personal blog&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>docker</category>
    </item>
    <item>
      <title>Github, Python and Docker: The Sweet Trio</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Wed, 09 Jun 2021 16:18:05 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/github-python-and-docker-the-sweet-trio-18bc</link>
      <guid>https://dev.to/deepjyoti30/github-python-and-docker-the-sweet-trio-18bc</guid>
      <description>&lt;p&gt;I have been meaning to write about this for a while. Since most of my work revolves around building API's and deploying them, it is pretty important for me (and people like me) to automate as many things as possible in order to get the best out of my time and available resources.&lt;/p&gt;

&lt;p&gt;My current tech stack revolves around the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python (that wasn't hard to guess, huhh!?)&lt;/li&gt;
&lt;li&gt;FastAPI (For the API)&lt;/li&gt;
&lt;li&gt;Celery (For workers to do tasks in the background)&lt;/li&gt;
&lt;li&gt;Docker (Why not!?)&lt;/li&gt;
&lt;li&gt;GitHub (Version control)&lt;/li&gt;
&lt;li&gt;DigitalOcean (Server)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do the above fit in the bigger picture?
&lt;/h2&gt;

&lt;p&gt;I will try to keep things as simple as possible.&lt;/p&gt;

&lt;p&gt;I use Python as my primary language to build API's. FastAPI is a Python framework that makes building production API's way simpler. Celery is a Python tool/library that queues tasks and does them in the background.&lt;/p&gt;

&lt;p&gt;Using the above three makes my API fast and production ready.&lt;/p&gt;

&lt;p&gt;I use Docker for containerizing my API. The code for the API and Docker is stored on GitHub. Finally, the docker container is deployed on a VPS on DigitalOcean so that my API can be interected with by the people and most importantly, my apps!&lt;/p&gt;

&lt;p&gt;When all of them are used together, I have a fast, production ready API that is accessible by my apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typical Scenario
&lt;/h2&gt;

&lt;p&gt;Let's consider two locations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My PC (where I write code)&lt;/li&gt;
&lt;li&gt;The server (where the API is deployed)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order for my code to go from the first destination (my PC) to the second destination (my server), it has to go through a few steps.&lt;/p&gt;

&lt;p&gt;Now, let's say we don't know something called pipeline exists. A typical way for a code change to go from the start point to the destination would be the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Code pushed to GitHub.&lt;/li&gt;
&lt;li&gt;I ssh into my server.&lt;/li&gt;
&lt;li&gt;Pull the latest code changes.&lt;/li&gt;
&lt;li&gt;Build the latest docker image.&lt;/li&gt;
&lt;li&gt;Deploy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a simple question, out of all the above steps, which ones require me to do them manually or even a part of them manually?&lt;/p&gt;

&lt;p&gt;I need to push the code &lt;strong&gt;myself&lt;/strong&gt;, I need to ssh into my server &lt;strong&gt;myself&lt;/strong&gt;. I need to pull the latest code changes &lt;strong&gt;myself&lt;/strong&gt;. I need to build the docker image &lt;strong&gt;myself&lt;/strong&gt;. I need to deploy the docker image &lt;strong&gt;myself&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, as it turns out, all the steps require me to give them some manual input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove redundant steps
&lt;/h2&gt;

&lt;p&gt;Now, out of the above, lets try to filter out the redundant steps. Redundant steps are those that can be executed by my computer with minimal interaction (can be passed through env variables) for me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Redundant steps are those that can be executed by my computer with minimal interaction (can be passed through env variables) for me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;We can make ssh into server automated since we just need to pass an ssh key.&lt;/li&gt;
&lt;li&gt;We can automate pulling the latest code changes.&lt;/li&gt;
&lt;li&gt;We can automate the docker image build and push process since it's the same step being repeated.&lt;/li&gt;
&lt;li&gt;We can automate the deployment of the latest docker image since that doesn't require any input.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is GitHub Actions (or anything similar)?
&lt;/h2&gt;

&lt;p&gt;GitHub Actions is a Continuous Integration service. It is very much similar to Travis CI and Jenkins but it is built by GitHub so that obviously comes with perks if we're using GitHub.&lt;/p&gt;

&lt;p&gt;We pass GitHub Actions a few steps to follow (remember those redundant steps?). It will gracefully follow all the steps and the end result should be our code being deployed.&lt;/p&gt;

&lt;p&gt;Basically, we are defining a path that our code will follow in order to reach their destination (the server) but &lt;strong&gt;without any human interaction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;One thing we need to keep in mind is that GitHub Actions will actually run the steps we ask it to run in a server somewhere but not in our server neither in our computer. This is why we say &lt;em&gt;pipelines build on the fly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We can easily specify which OS GitHub Action uses in order to run the steps. (More on that below).&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we tell GitHub Actions to do something?
&lt;/h3&gt;

&lt;p&gt;In order to let GitHub know that we want it to follow a few steps and do an &lt;em&gt;action&lt;/em&gt;, we can do that by passing a &lt;code&gt;yaml&lt;/code&gt; file. This file needs to follow the &lt;a href="https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions"&gt;GitHub workflow syntax&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We can define jobs in the file. Each job can have a few steps that are supposed to be followed. Jobs can be defined in the following way:&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Name of the job&lt;/span&gt;
  &lt;span class="na"&gt;push-to-docker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt; &lt;span class="c1"&gt;# Use latest Ubuntu for the steps&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Do something&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;Do something&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Removing redundant steps
&lt;/h2&gt;

&lt;p&gt;Now that we know that we can use GitHub Actions to automate our steps, let's see how we can make sure that GitHub Actions follows each step.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;NOTE: From now on, we need to keep in mind that the steps are being written for GitHub Actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Automate pulling the latest code changes
&lt;/h3&gt;

&lt;p&gt;There is a neat action that is provided by GitHub itself. &lt;a href="https://github.com/actions/checkout"&gt;GitHub Actions Checkout&lt;/a&gt; checks out the latest code from the repository we're running the action on.&lt;/p&gt;

&lt;p&gt;This means, we can just use this package to pull our latest changes. Now in whatever server GitHub Action is running, we will basically clone the latest changes from our repo to that server.&lt;/p&gt;

&lt;p&gt;Following is the code to tell Actions to checkout the latest code from the current repo.&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;example-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Checkout the code&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;Checkout Repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code tells action to start a new job named &lt;code&gt;example-job&lt;/code&gt; on the &lt;code&gt;latest version of Ubuntu&lt;/code&gt; and run the following steps.&lt;/p&gt;

&lt;p&gt;The first step pulls the latest changes of the current repo to that OS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate building the docker image and pushing it
&lt;/h3&gt;

&lt;p&gt;Now, since we have the latest code changes avaiable, we can easily build a docker image and push it to Docker Hub or GitHub Container Registry.&lt;/p&gt;

&lt;p&gt;In order to do that, we can use the package &lt;a href="https://github.com/docker/build-push-action"&gt;build-push-action&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, docker or GitHub doesn't just let anyone push an image to the container registry. This means, we need to login to docker in order to push the latest built image. We can do that by using the &lt;a href="https://github.com/docker/login-action"&gt;login-action&lt;/a&gt; package from docker.&lt;/p&gt;

&lt;p&gt;Adding the above steps to the yaml file will make it look like following:&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
   &lt;span class="na"&gt;example-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Checkout the code&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;Checkout Repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="c1"&gt;# Login to docker&lt;/span&gt;
      &lt;span class="c1"&gt;# This can be used for both Docker Hub and&lt;/span&gt;
      &lt;span class="c1"&gt;# GitHub container registry.&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;Login to GitHub Container Registry&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;docker/login-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Remove the following line if you want to&lt;/span&gt;
          &lt;span class="c1"&gt;# login to docker hub.&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&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;${{ github.repository_owner }}&lt;/span&gt;
          &lt;span class="c1"&gt;# secrets are GitHub actions that can be added&lt;/span&gt;
          &lt;span class="c1"&gt;# from settings of the repo.&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CR_PAT }}&lt;/span&gt;
      &lt;span class="c1"&gt;# Build the docker image and push it.&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 image&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;docker/build-push-action@v2&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;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
          &lt;span class="c1"&gt;# Remove the `gchr.io/` if you're pushing to&lt;/span&gt;
          &lt;span class="c1"&gt;# Docker Hub&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/owner/packageName:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above code, we have already removed three steps that we were doing manually. Now we can just ssh into our server, pull the latest image and deploy it. But wait a minute, shouldn't we automate that too?&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate SSH into server and deploy latest image
&lt;/h3&gt;

&lt;p&gt;Now we have just one more step left. We want our GitHub Action to deploy the latest image in our server as well. Since our action is already running on a machine, can't we just pass a few steps to make the machine ssh into the actual server (the destination) and then deploy the changes?&lt;/p&gt;

&lt;p&gt;Yes we can! We will use the &lt;a href="https://github.com/appleboy/ssh-action"&gt;ssh action&lt;/a&gt; package for that. This package will do what we have been doing manually. SSH into the server and deploy.&lt;/p&gt;

&lt;p&gt;Let's add a new job that will wait for our first job to complete and then ssh into our server and deploy the changes:&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="c1"&gt;# Name of the job is deploy&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;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;job1&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="c1"&gt;# SSH into the server&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;SSH into server&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="c1"&gt;# The server IP of our VPS from DigitalOcean&lt;/span&gt;
          &lt;span class="c1"&gt;# or any other provider. This is again&lt;/span&gt;
          &lt;span class="c1"&gt;# passed in as a secret.&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.SERVER_IP }}&lt;/span&gt;
          &lt;span class="c1"&gt;# Username of the user sshing into the server&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.SERVER_USERNAME }}&lt;/span&gt;
          &lt;span class="c1"&gt;# The private ssh key in order to get access&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.KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker pull ghcr.io/owner/packageName:latest &amp;amp;&amp;amp; docker run ghcr.io/owner/packageName:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the above job takes care of deploying as well.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that you might have a more complex deploy script for Docker in order to consider things like if the container is already running and ports etc.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Final steps
&lt;/h2&gt;

&lt;p&gt;Now that we have our steps file in place, we need to tell GitHub to run it when we push something. We can do that by saving the file in the &lt;code&gt;.github/workflows/&lt;/code&gt; directory of our project. So if the name of the file is &lt;code&gt;build.yml&lt;/code&gt;, we will have the following directory structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- project
  |-- .github
    |-- workflows
      |-- build.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the file is located like above, it will be automatically picked up by GitHub. However, we might not want the action to run when we push to certain branches. The file should just run on a few branches (let's say &lt;code&gt;staging&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt;), we can do that by the following:&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;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staging"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also give the action a name by the &lt;code&gt;name&lt;/code&gt; syntax:&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 API and Deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Adding secrets
&lt;/h3&gt;

&lt;p&gt;Now, you might not want sensitive data to be passed on along with the code. Especially if the repo is public. In a case like this, we can use GitHub's secrets feature to store sensitive data like &lt;code&gt;ssh private key&lt;/code&gt; etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/57685065/how-to-set-secrets-in-github-actions"&gt;This question on StackOverflow explains how to add secrets on GitHub&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We started with 5 steps, each one of them required manual interaction. Now we are left with one step (git commit) that requires interaction. All the other steps are automatically handled by our CI service (GitHub). CI is a very useful aspect of deployment, especially if you're a web developer.&lt;/p&gt;

&lt;p&gt;When we add all the jobs and steps to one file, it turns out as following:&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 API and Deploy&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;staging"&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;example-job&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Checkout the code&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;Checkout Repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="c1"&gt;# Login to docker&lt;/span&gt;
      &lt;span class="c1"&gt;# This can be used for both Docker Hub and&lt;/span&gt;
      &lt;span class="c1"&gt;# GitHub container registry.&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;Login to GitHub Container Registry&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;docker/login-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Remove the following line if you want to&lt;/span&gt;
          &lt;span class="c1"&gt;# login to docker hub.&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&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;${{ github.repository_owner }}&lt;/span&gt;
          &lt;span class="c1"&gt;# secrets are GitHub actions that can be added&lt;/span&gt;
          &lt;span class="c1"&gt;# from settings of the repo.&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CR_PAT }}&lt;/span&gt;
      &lt;span class="c1"&gt;# Build the docker image and push it.&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 image&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;docker/build-push-action@v2&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;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
          &lt;span class="c1"&gt;# Remove the `gchr.io/` if you're pushing to&lt;/span&gt;
          &lt;span class="c1"&gt;# Docker Hub&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/owner/packageName:latest&lt;/span&gt;
  &lt;span class="c1"&gt;# Name of the job is deploy&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;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example-job&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="c1"&gt;# SSH into the server&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;SSH into server&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="c1"&gt;# The server IP of our VPS from DigitalOcean&lt;/span&gt;
          &lt;span class="c1"&gt;# or any other provider. This is again&lt;/span&gt;
          &lt;span class="c1"&gt;# passed in as a secret.&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.SERVER_IP }}&lt;/span&gt;
          &lt;span class="c1"&gt;# Username of the user sshing into the server&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.SERVER_USERNAME }}&lt;/span&gt;
          &lt;span class="c1"&gt;# The private ssh key in order to get access&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.KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker pull ghcr.io/owner/packageName:latest &amp;amp;&amp;amp; docker run ghcr.io/owner/packageName:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding the above workflow requires just a few seconds but it highly removes the manual work for me. I can now just make some changes and merge them to a branch and it will be automatically picked up by my pipeline and deployed in a matter of seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/deepjyoti30/ytmdl-web-v2/blob/master/.github/workflows/build-push.yml"&gt;If you'd like to see one of these in action&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CI can also be used for things like testing. The actions can be run on various events like &lt;em&gt;Pull Requests&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://blog.deepjyoti30.dev/github-actions-pipeline"&gt;This article was originally posted at my personal blog&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>docker</category>
      <category>github</category>
    </item>
    <item>
      <title>Easily Test Mixins With Jest</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Fri, 14 May 2021 10:23:16 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/easily-test-mixins-with-jest-5e49</link>
      <guid>https://dev.to/deepjyoti30/easily-test-mixins-with-jest-5e49</guid>
      <description>&lt;p&gt;So recently I decided to add tests to one of my &lt;a href="https://github.com/deepjyoti30/ytmdl-web-v2"&gt;repos&lt;/a&gt;. Since I am new to JS to some extent, I started looking for what the best ways were to add tests to a frontend project. Well, as it turns out there are many libraries (duhh, it's JS afterall). However, there are various types of testing as well like &lt;strong&gt;Unit Testing&lt;/strong&gt; or &lt;strong&gt;Component Testing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post is not about those tests though. After looking a bit for the best library to use with VueJS, I decided on using &lt;a href="https://jestjs.io/"&gt;jest&lt;/a&gt;. I am not an expert but it doesn't take an expert to know that Jest was built for &lt;strong&gt;ReactJS&lt;/strong&gt;. Well, for starter, Jest is built by Facebook and &lt;code&gt;has a great integration with React. js&lt;/code&gt;. So yeah, Jest might not be the first choice of Vue users.&lt;/p&gt;

&lt;p&gt;I loved the fact that it was so easy to setup and as I started writing tests I understood that it's actually very easy to use as well. So all in all, I ended up using Jest as the testing framework for my project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Mixins
&lt;/h2&gt;

&lt;p&gt;After I had written tests for most of my components, I finally decided to write tests for my mixins. This is when I came to a stop. I started looking at the docs in order to know what the best way to test mixins would be. However, there wasn't much documentation regarding that.&lt;/p&gt;

&lt;p&gt;This is when I realized something.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are mixins?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;A mixin is a class containing methods that can be used by other classes without a need to inherit from it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As &lt;a href="https://en.wikipedia.org/wiki/Mixin"&gt;stated on Wikipedia&lt;/a&gt;, &lt;em&gt;a mixin is a class containing methods that can be used by other classes without a need to inherit from it&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In case you are not familiar to mixins (why are you even reading this article about writing tests for mixins then?), mixins are functions that developers can &lt;em&gt;inherit&lt;/em&gt; in some other code and use them.&lt;/p&gt;

&lt;p&gt;This also means that &lt;strong&gt;mixins cannot be used independently&lt;/strong&gt;. What I mean is, let's say you have a mixin defined in VueJS. Now you want to use this mixin. You can easily import it and use. However, in your source code is there any use of the mixin without it getting imported? No.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mock Component
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;We will create a mock component, a bare bones component basically, to inherit the mixin to that and test it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that our doubt about mixins is out of the way. It is clear, we will &lt;strong&gt;need&lt;/strong&gt; a component that can import the Mixin in order to test it. So what should we do?&lt;/p&gt;

&lt;p&gt;We can just make a simple Mock Component. In my case, I went with a simple Mock Component written in Vue in the following way:&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/template&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Return a component to be used as dummy in order
 * to mock things like mixins.
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MockComponent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we have a component, we can easily use it to use our mixins and use it accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using mock component with Mixin
&lt;/h2&gt;

&lt;p&gt;Let's say we have a mixin with a function &lt;code&gt;getBoolFromValue()&lt;/code&gt;, we can easily write a jest test for that now:&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="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test mixin function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shallowMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;MockComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;mixins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mixinName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return proper value from bool&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getBoolFromValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;on&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why not use a component from the code?
&lt;/h2&gt;

&lt;p&gt;Well, using a component already defined in the code would work too. However, why add all the bulk while testing a mixin. We want the mixin to be tested as easily as possible. As well, adding a component might add some bulky imports etc, so it is way easier to just keep a &lt;code&gt;MockComponent&lt;/code&gt; that is just basically an empty template to test Mixins.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/deepjyoti30/ytmdl-web-v2/blob/master/tests/__tests__/mixins.settings.test.js"&gt;You can check the tests on my repo as well&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  This post was initally posted on my &lt;a href="https://blog.deepjyoti30.dev/mixins-test-jest"&gt;personal blog&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>vue</category>
      <category>mixin</category>
    </item>
    <item>
      <title>Deploy Celery &amp; RabbitMQ with Compose</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Wed, 07 Apr 2021 13:42:48 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/deploy-celery-rabbitmq-with-compose-4pfe</link>
      <guid>https://dev.to/deepjyoti30/deploy-celery-rabbitmq-with-compose-4pfe</guid>
      <description>&lt;p&gt;Off late, I had been working a lot with FastAPI. Recently, however I had a requirement. I needed to run tasks in the background after the request was made. So something like, when the request is recieved, add it to the task list and return a response. The task will be done in the background.&lt;/p&gt;

&lt;p&gt;I somewhat knew about &lt;a href="https://docs.celeryproject.org/"&gt;Celery&lt;/a&gt;, however never had the need to work with it. So I finally decided to use Celery and boy oh boy, was I surprised.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Celery
&lt;/h2&gt;

&lt;p&gt;So, Celery is a task scheduler. What I described above in the first paragraph, Celery does exactly that! We pass it a task and it runs that in the background.&lt;/p&gt;

&lt;p&gt;It basically runs the task as a synchornous function which effectively makes the function run in the background. &lt;a href="https://docs.celeryproject.org/en/stable/getting-started/introduction.html"&gt;You can read more about it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;However, in order for Celery to run properly, it needs a &lt;strong&gt;broker&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a broker?
&lt;/h3&gt;

&lt;p&gt;A broker will keep a list of all the tasks that are to be executed and will accordingly supply Celery with the tasks. Celery will then use the task and work on it.&lt;/p&gt;

&lt;p&gt;Directly from Google:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A broker is a person or firm who arranges transactions between a buyer and a seller for a commission when the deal is executed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This pretty much sums it up. In our case, we are the &lt;strong&gt;seller&lt;/strong&gt;, celery is the &lt;strong&gt;buyer&lt;/strong&gt; and we will use a &lt;strong&gt;broker&lt;/strong&gt; in between in order to handle all the tasks. Brokers are also called as message queues or task queues.&lt;/p&gt;

&lt;p&gt;Some of the brokers that Celery works with are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  RabbitMQ
&lt;/h3&gt;

&lt;p&gt;In this article, I will primarily use &lt;strong&gt;RabbitMQ&lt;/strong&gt; as the broker. You can read on how to &lt;a href="https://docs.celeryproject.org/en/stable/getting-started/brokers/redis.html"&gt;use Redis with Celery&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Easiest way to setup RabbitMQ is to use a docker file. Using the following command, a container with RabbitMQ can be deployed within seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;docker run -d --rm -it --hostname my-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-management
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In the above command, the &lt;code&gt;management&lt;/code&gt; image is used. You can check &lt;a href="https://hub.docker.com/_/rabbitmq"&gt;other available images here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Breaking down the above command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We are mapping the &lt;code&gt;15672&lt;/code&gt; port of the container to our host&lt;/li&gt;
&lt;li&gt;We are mapping the &lt;code&gt;5672&lt;/code&gt; port of the container to our host.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is because the &lt;code&gt;15672&lt;/code&gt; port serves the GUI for rabbitmq and &lt;code&gt;5672&lt;/code&gt; is how &lt;strong&gt;Celery&lt;/strong&gt; will communicate with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Worker
&lt;/h2&gt;

&lt;p&gt;Now that we have our broker in place, let's use a Dockerfile to deploy &lt;strong&gt;Celery&lt;/strong&gt;. Celery, since it does tasks in the background, is referred to as &lt;strong&gt;worker&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will build the &lt;code&gt;worker&lt;/code&gt; with the following Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.6&lt;/span&gt;

&lt;span class="c"&gt;# copy contents of project into docker&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ./ /app/&lt;/span&gt;

&lt;span class="c"&gt;# We will use internal functions of the API&lt;/span&gt;
&lt;span class="c"&gt;# So install all dependencies of the API&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; celery -A worker worker --loglevel=INFO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the above Dockerfile, we can deploy the worker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using a compose file
&lt;/h2&gt;

&lt;p&gt;Now that we have two of the services ready, we are ready to write our &lt;strong&gt;docker compose&lt;/strong&gt; file. &lt;a href="https://docs.docker.com/compose/gettingstarted/"&gt;Read more about docker compose here&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Usually, the worker is run along with an API and the API makes calls to the worker in order to run the worker tasks in the background.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In our case, we will be creating two containers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RabbitMQ container&lt;/li&gt;
&lt;li&gt;Worker container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We want our worker to access the rabbitMQ container through the network and accordingly use it as a broker.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Most of the time, you'll probably also need an API container that will also interact with the worker using the network.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Following is the compose file:&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;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.7"&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Deploy the broker.&lt;/span&gt;
  &lt;span class="na"&gt;rabbitmq_server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rabbitmq:3-management&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Expose the port for the worker to add/get tasks&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5672:5672&lt;/span&gt;
      &lt;span class="c1"&gt;# OPTIONAL: Expose the GUI port&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;15672:15672&lt;/span&gt;

  &lt;span class="c1"&gt;# Deploy the worker&lt;/span&gt;
  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Build using the worker Dockerfile&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;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;dockerfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker.Dockerfile&lt;/span&gt;
    &lt;span class="c1"&gt;# Need to access the database&lt;/span&gt;
    &lt;span class="c1"&gt;# OPTIONAL: If you worker needs to access your db that is deployed&lt;/span&gt;
    &lt;span class="c1"&gt;# locally, then make the network mode as host.&lt;/span&gt;
    &lt;span class="na"&gt;network_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;host&lt;/span&gt;
    &lt;span class="c1"&gt;# Pass the rabbitmq_uri as env varible in order to&lt;/span&gt;
    &lt;span class="c1"&gt;# connect to our service&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# NOTE: Below we are using 127.0.0.1 because this container&lt;/span&gt;
      &lt;span class="c1"&gt;# will run on the host network, thus it will have access to the&lt;/span&gt;
      &lt;span class="c1"&gt;# host network.&lt;/span&gt;
      &lt;span class="c1"&gt;# If it would not have run locally, we would have had to&lt;/span&gt;
      &lt;span class="c1"&gt;# connect using the service name like following:&lt;/span&gt;
      &lt;span class="c1"&gt;# amqp:rabbitmq_server:5672&lt;/span&gt;
      &lt;span class="na"&gt;rabbitmq_uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;amqp://127.0.0.1:5672&lt;/span&gt;
    &lt;span class="c1"&gt;# Make it wait for rabbitmq deployment&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rabbitmq_server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the above file, you can deploy it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;docker-compose -f docker-compose.yml up --detach --scale worker=2 --build
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;In the above command, we are scaling the &lt;code&gt;worker&lt;/code&gt; service to have 2 containers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Gotcha's to look out for
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Connection URI for RabbitMQ
&lt;/h3&gt;

&lt;p&gt;Let's say we have RabbitMQ deployed in a container called &lt;code&gt;rabbitmq&lt;/code&gt;. Now, from our &lt;code&gt;worker&lt;/code&gt; container we need to access RabbitMQ in order to add tasks. In this case, we will have to connect to RabbitMQ using a connection URI. This URI will be something like:&lt;br&gt;
&lt;/p&gt;

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


&amp;gt; Note that we have name of the container in the URI. This will map the URI to the network of that container.

Typically this URI should be something like `amqp://localhost:5672`

However, now, let's say we need to run our container in the network. This can be easily done using the `network_mode: host` field in the compose file or the `--network=host` arguement to the deploy command.

In cases like this, our container will have the network of the host which means the RabbitMQ container will be accessible as it will be accessible to the network which will be:



```amqp://127.0.0.1:5672```



&amp;gt; Note that we exposed the port 5672 when deploying the rabbitmq container.

This article is posted on [my personal blogging site](https://blog.deepjyoti30.dev/celery_compose)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>python</category>
      <category>celery</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Automate Tests With GitHub Actions for Python</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Fri, 26 Mar 2021 06:16:44 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/automate-tests-with-github-actions-for-python-31e8</link>
      <guid>https://dev.to/deepjyoti30/automate-tests-with-github-actions-for-python-31e8</guid>
      <description>&lt;p&gt;Since the addition of GitHub Actions, a lot of things, that previsouly we had to use third party services for, have become easier. Recently, an user raised an issue in my &lt;a href="https://github.com/deepjyoti30/downloader-cli"&gt;downloader-cli&lt;/a&gt; repository. He suggested that addition of tests would be greatly helpful. More importantly because that way when people make a PR, the tests would tell the developers if the PR can be accepted.&lt;/p&gt;

&lt;p&gt;So, I decided to add some nice tests for my app. &lt;code&gt;downloader-cli&lt;/code&gt; is written in Python and it is a small, simple, downloader library that features a highly customizable, responsive progressbar for the commandline.&lt;/p&gt;

&lt;h2&gt;
  
  
  How will it work
&lt;/h2&gt;

&lt;p&gt;The idea is simple. We will add a GitHub workflow file. This file will have the details as to when and what to do. For the sake of this example, I will make the tests run when a push is made to the &lt;code&gt;master&lt;/code&gt; branch or a pull request is made to the &lt;code&gt;master&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;In case you don't already know, GitHub's workflow files are written in &lt;a href="https://yaml.org/"&gt;YAML&lt;/a&gt; and are pretty simple to get started with.&lt;/p&gt;

&lt;p&gt;You just need to create a directory named &lt;code&gt;.github&lt;/code&gt; and a subdirectory inside that named &lt;code&gt;workflows&lt;/code&gt;. Inside the subdirectory, you can add as many workflow files as you want, given that all of them follow proper syntax and are written in &lt;strong&gt;YAML&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Conditions to run
&lt;/h2&gt;

&lt;p&gt;We will create a file named &lt;code&gt;tests.yml&lt;/code&gt; and add the basic conditions to make it run on PR's and Pushes.&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;Run tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add condition to run on &lt;code&gt;push&lt;/code&gt; on branch &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add condition to run on &lt;code&gt;pull_request&lt;/code&gt; on branch &lt;code&gt;master&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Add the job
&lt;/h2&gt;

&lt;p&gt;So once we tell GitHub about when to run the action, we also need to pass them some commands on what to do when running the action. These can be called as &lt;code&gt;jobs&lt;/code&gt;. One action file can have multiple jobs and jobs can depend on other jobs so that they will run only after the job they depend on is run.&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Run in all these versions of Python&lt;/span&gt;
        &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;3.5&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.6&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.7&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.8&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;3.9&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Checkout the latest code from the repo&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;Checkout repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
        &lt;span class="c1"&gt;# Setup which version of Python to use&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set Up Python ${{ matrix.python-version }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.python-version }}&lt;/span&gt;
        &lt;span class="c1"&gt;# Display the Python version being used&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;Display Python version&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python -c "import sys; print(sys.version)"&lt;/span&gt;
        &lt;span class="c1"&gt;# Install the package using the setup.py&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install package&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python setup.py install&lt;/span&gt;
        &lt;span class="c1"&gt;# Install pytest (you can use some other testing utility)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install pytest&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
          &lt;span class="s"&gt;pip install pytest&lt;/span&gt;
        &lt;span class="c1"&gt;# Run the tests. I'm using pytest and the file is in the tests directory.&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;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pytest tests/test*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code, we add a job with the name &lt;code&gt;test&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This job &lt;code&gt;runs-on&lt;/code&gt; the latest Ubuntu Version as deonoted by &lt;code&gt;ubuntu-latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We are using the &lt;code&gt;matrix&lt;/code&gt; strategy to run it for different python versions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we have added a few steps. The steps do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checkout the latest code from the repo&lt;/li&gt;
&lt;li&gt;Setup the &lt;code&gt;python-version&lt;/code&gt; as passed by the matrix strategy.&lt;/li&gt;
&lt;li&gt;Display the python version (just cross check).&lt;/li&gt;
&lt;li&gt;Install the package. (In my case it is using the &lt;code&gt;setup.py&lt;/code&gt; file.)&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;pytest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run the tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Push the code
&lt;/h2&gt;

&lt;p&gt;Once you have the file setup, you can try try out the changes by pushing the code to the master branch.&lt;/p&gt;

&lt;p&gt;Just do a push using &lt;code&gt;git&lt;/code&gt; and go to GitHub and open the &lt;code&gt;Actions&lt;/code&gt; tab. You will see a job running with the yellow indicator on the left. (The yellow means it is running).&lt;/p&gt;

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

&lt;p&gt;If you want to take a look at the actions of my repo, you can &lt;a href="https://github.com/deepjyoti30/downloader-cli/actions"&gt;check them here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, GitHub Actions can be used for various automations. Most useful usecase (at least for me) being the ability to add CI/CD pipelines which have made a lot of my deployments painless. You can see an example of that &lt;a href="https://github.com/deepjyoti30/ytmdl-web-v2/blob/master/.github/workflows/build-push.yml"&gt;in my ytmdl-web repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, in case you want to take a look at the file from my &lt;code&gt;downloader-cli&lt;/code&gt; repo, you can &lt;a href="https://github.com/deepjyoti30/downloader-cli/blob/master/.github/workflows/test.yml"&gt;check it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post was originally published at my &lt;a href="https://blog.deepjyoti30.dev"&gt;personal blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>github</category>
      <category>devops</category>
    </item>
    <item>
      <title>I rebuilt my blog with Nuxt, now it loads in seconds!</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Sat, 20 Mar 2021 16:51:35 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/i-rebuilt-my-blog-with-nuxt-now-it-loads-in-seconds-24i4</link>
      <guid>https://dev.to/deepjyoti30/i-rebuilt-my-blog-with-nuxt-now-it-loads-in-seconds-24i4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;TL;DR: Rebuilt my blog (this page) with Nuxt in order to improve SEO and performance and give the whole thing a new look. Used Tailwind too (well, duh!)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I felt like it was about time I tried my hands on Nuxt. Considering that Vue is my goto frontend framework, Nuxt was inevitable to be skipped. Though, I cannot just &lt;em&gt;learn&lt;/em&gt; a language, I need to build something in order to get to know the language all around. So I started wondering what would be a perfect project to build with Nuxt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Nuxt?
&lt;/h2&gt;

&lt;p&gt;So in order to know what project would be perfect for Nuxt, I started researching a bit on why Nuxt would be better as compared to Vue. Obviously, I came across &lt;em&gt;SSR&lt;/em&gt; a lot more that I expected to. So it was time, I dived into SSR and built something that could benefit from it.&lt;/p&gt;

&lt;p&gt;If you would just do a google search &lt;code&gt;Why is SSR good&lt;/code&gt;, you'd be easily prompted to a lot of sites, however all these sites will have one thing in common, &lt;em&gt;SSR is good for SEO&lt;/em&gt;. You see where this is going?&lt;/p&gt;

&lt;p&gt;Out of all the apps that I have currently, this blog is the one that will be able to leverage SEO the most and yeah I was kinda bored of the old design, really started disliking it after a while.&lt;/p&gt;

&lt;p&gt;So that was that. I started rebuilding my blog. The backend is still the same. The same old API built on FastAPI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;Yeah, I had to list down the features that I think are way better in this version. Some of the key highlights are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Way faster&lt;/li&gt;
&lt;li&gt;Improved SEO&lt;/li&gt;
&lt;li&gt;Better theme&lt;/li&gt;
&lt;li&gt;Better dark mode&lt;/li&gt;
&lt;li&gt;Comments (thanks to &lt;a href="https://utteranc.es/"&gt;utteranc.es&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Everything is better&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Yeah, I honestly think this version is pretty good. I mean the older one was not bad by any means, but a lot of things were just not perfect for a blog. Things like the SEO takes the whole page a long way along.&lt;/p&gt;

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

&lt;p&gt;Since I have recently started using Tailwind, I went with tailwind again and I have to admit, I like it a bit more every time I use it. It just makes so many things so much easier. Things like handling a dark theme. Talking about dark themes, consider checking out the dark theme. Just open the menu (if you're on mobile) and click on the sun icon.&lt;/p&gt;

&lt;p&gt;The themes are also set based on the device theme (if the user has not explicitly set a theme). I wanted to keep an option with &lt;em&gt;auto&lt;/em&gt; that would theme the site based on the users mobile, however, I just couldn't find the motivation to add that. Perhaps in the next release?!&lt;/p&gt;

&lt;h3&gt;
  
  
  Comments
&lt;/h3&gt;

&lt;p&gt;Okay, this is a big one. I was just going through a blog site and I came accross &lt;a href="https://utteranc.es/"&gt;utterances&lt;/a&gt;, and I have to say it's a pretty neat idea. Like the whole thing is very easy to setup.&lt;/p&gt;

&lt;p&gt;However, I will have to call it both an &lt;em&gt;advantange&lt;/em&gt; and a &lt;em&gt;disadvantage&lt;/em&gt; that it is built on GitHub. This means, if the blog has readers that do not have a GitHub account, would not be able to comment.&lt;/p&gt;

&lt;p&gt;However, if the blog has readers that are mostly &lt;em&gt;techies&lt;/em&gt; (read people with GitHub account), then it is like a wish come true. I think being built on GitHub, it is like an extra push for &lt;em&gt;techies&lt;/em&gt; to comment.&lt;/p&gt;

&lt;p&gt;But, it is what it is, it depends on the readers whether it would be a good idea.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redesigned share menu
&lt;/h3&gt;

&lt;p&gt;So, the share menu is now redesigned. It still continues the old approach however. If your device supports native share, then the custom menu will not be shown. However, most PC's does not really support it (can't vouch for Windows, totally doesn't work on i3-gaps), so the custom menu is a great addition.&lt;/p&gt;

&lt;p&gt;Check it out by clicking on the share button at the end of the post, or at the top of the post.&lt;/p&gt;

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

&lt;p&gt;I think, all things, considered, this was a pretty good chance for me to learn Nuxt and I totally enjoyed. I am already considering Nuxt for my upcoming projects. It's ofcourse good for SEO, however the whole SSR thing makes it very convinient to make the site performant. Also, really appreciate that the app is automatically built as a &lt;em&gt;PWA&lt;/em&gt;. Anyway, some more posts coming up in the following days, consider subscirbing to the newsletter!&lt;/p&gt;

&lt;p&gt;Also, the repo is now open source, in case you are interested, &lt;a href="https://github.com/deepjyoti30/blog-frontend-v2"&gt;check it out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blog.deepjyoti30.dev/blog-redesign-nuxt"&gt;This post was originally posted at my blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>vue</category>
      <category>performance</category>
      <category>showdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Python has a built in search function. Here's how to use it</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Mon, 04 Jan 2021 14:58:27 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/python-has-a-built-in-search-function-here-s-how-to-use-it-2joa</link>
      <guid>https://dev.to/deepjyoti30/python-has-a-built-in-search-function-here-s-how-to-use-it-2joa</guid>
      <description>&lt;p&gt;I was going through the Python docs and guess what I came across? A function that helps you get closest matches to a list of other strings. Now why would that function be useful, you'd ask. Let me give you a proper example.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem
&lt;/h3&gt;

&lt;p&gt;About 1 and a half years ago, I was working on playx. It is a tool that lets you stream music from the commandline directly from YouTube. While working on it, we decided that adding a feature to cache the files would be pretty nice. Now, while implementing that, we came across a problem. We needed to check if a song is already cached.&lt;/p&gt;

&lt;p&gt;Now, the issue is, on YouTube, one song is uploaded by various channels which means the titles vary and since we were caching the songs using the title of the video from YouTube, that meant that if we did a direct match of the titles, it would be a very inefficient way to check if the song has been cached already.&lt;/p&gt;

&lt;p&gt;For example, if we have the song name &lt;code&gt;Sundown&lt;/code&gt;. This song might have been saved as &lt;code&gt;Sundown - Gordon Lightfoot&lt;/code&gt;. However, the next time user tries to play the same song which had a different title, something like &lt;code&gt;Sundown - Lyrics only | Gordon Lightfoot&lt;/code&gt;. In this case, a simple match would indicate that both songs are different and the tool would end up caching both even though the songs are same.&lt;/p&gt;

&lt;p&gt;If you got an idea of the problem from reading the above, I think you would agree with me that it is similar to searching a particular file in a list of files.&lt;/p&gt;

&lt;h2&gt;
  
  
  This was originally posted on my personal blog and can be read &lt;a href="https://blog.deepjyoti30.dev/pythons-builtin-search-function"&gt;here&lt;/a&gt;
&lt;/h2&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>Happiness status of your GitHub repo: repostatus</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Wed, 09 Dec 2020 14:21:23 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/happiness-status-of-your-github-repo-repostatus-3516</link>
      <guid>https://dev.to/deepjyoti30/happiness-status-of-your-github-repo-repostatus-3516</guid>
      <description>&lt;p&gt;People always ask "why this repo" and never ask "how is this repo", so I created an app that finds how &lt;strong&gt;happy&lt;/strong&gt; a repository is.&lt;/p&gt;

&lt;p&gt;TLDR; The app runs a sentiment analysis engine on your repo and finds how happy it is. Check &lt;a href="https://repostatus.deepjyoti30.dev" rel="noopener noreferrer"&gt;repostatus&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How?
&lt;/h2&gt;

&lt;p&gt;So how exactly do you find the sentiment of a non living thing? Well, even I had that thought in my mind. Any repository is made up of people that contribute to that repo, people that interact in the repo's comments.&lt;/p&gt;

&lt;p&gt;So, if we are able to run a sentiment analysis engine on the interactions of the people that are contributing to that repo, we might get somewhere?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;repostatus&lt;/code&gt; extracts three important part of the repo (by using GitHub's API):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the commit messages&lt;/li&gt;
&lt;li&gt;the comments on the issues&lt;/li&gt;
&lt;li&gt;the comments on PR's&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once, we have these three things, we can combine them, filter out the unnecessary data and run our engine on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tech
&lt;/h2&gt;

&lt;p&gt;The backend is written in &lt;code&gt;Python&lt;/code&gt; (FastAPI) and the frontend is written in &lt;code&gt;Vue&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First things first,  the sentiment analysis engine used by  &lt;code&gt;repostatus&lt;/code&gt; is the &lt;a href="https://textblob.readthedocs.io/en/dev/" rel="noopener noreferrer"&gt;textblob&lt;/a&gt; library. It is very easy to use and works great.&lt;/p&gt;

&lt;p&gt;So now that we have the engine at our ease, what's next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backend
&lt;/h3&gt;

&lt;p&gt;My goto tech stack is Python so no wonder I went with that for the API.&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; for the backend. Off late, I have started liking &lt;code&gt;FastAPI&lt;/code&gt; more and more and it was an obvious no brainer to go with it for the backend. It's ease of use with the efficiency is just awesome. If you haven't checked it out, do that, I'm sure you'll love it if you're a Python developer.&lt;/p&gt;

&lt;h4&gt;
  
  
  Services
&lt;/h4&gt;

&lt;p&gt;Services that the API will offer are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Internal API for the webapp&lt;/li&gt;
&lt;li&gt;Public API&lt;/li&gt;
&lt;li&gt;Badge API (Yep, you can use repostatus badges on your README).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted to provide a Public API so that people would be able to use it for their own fun projects. The API is capable of working with both private and public repo's. The details for that can be found &lt;a href="https://repostatus.deepjyoti30.dev/api" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The badge is another thing that I thought would be a nice little addition. This badge works similar to how the &lt;code&gt;travis&lt;/code&gt; build badges work or any badge. You can simply use the URL to embed it into your repositories README.&lt;/p&gt;

&lt;p&gt;More details about the badge can be found &lt;a href="https://repostatus.deepjyoti30.dev/badge" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frontend
&lt;/h3&gt;

&lt;p&gt;I love using VueJS. It was obvious that I would use that to build the frontend. Now for the frontend I wanted to make sure that it doesn't restrict the user too much.&lt;/p&gt;

&lt;p&gt;Thus, &lt;code&gt;repostatus&lt;/code&gt; works with both private and public repos. For private repos, GitHub's OAuth is used which gives us access to that particular repo and then we run the engine over the repo.&lt;/p&gt;

&lt;p&gt;One issue that I faced while implementing the OAuth was that I wanted to make the process seamless. If you go the the app now and select on the OAuth option, you will see the process is pretty neat.&lt;/p&gt;

&lt;p&gt;Here's what it does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Opens a new window and asks the user to give access&lt;/li&gt;
&lt;li&gt;User gives access and GitHub redirects the user to my sites &lt;code&gt;callback&lt;/code&gt; endpoint.&lt;/li&gt;
&lt;li&gt;Window closes and the app shows all the users repos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The above steps makes it look real seamless. However, the hard part for me was to figure out how do I know when the OAuth is done and then show the window.&lt;/p&gt;

&lt;h4&gt;
  
  
  Seamless OAuth
&lt;/h4&gt;

&lt;p&gt;So in order to make it seamless, I implemented the following flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User clicks on OAuth button, new window is opened and the app keeps waiting for it to close.&lt;/li&gt;
&lt;li&gt;In the new window, the user is redirected to the callback URL which returns a nice HTML page that shows the user that the window will close in 5 secs. After 5 seconds the window closes and the app knows that the OAuth is done.&lt;/li&gt;
&lt;li&gt;The app then tries to find the repo's of that user and accordingly shows the user all the repos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I know, it's not much. I have to say, though, I really liked implementing this one little feature and making it so seamless. I'm not even kidding, I just kept on doing OAuth on repeat after implementing it, for a while.&lt;/p&gt;

&lt;h2&gt;
  
  
  Badge
&lt;/h2&gt;

&lt;p&gt;An example of the badge can be seen 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%2Fapis.deepjyoti30.dev%2Frepostatus%2Fbadge%3Frepo%3Ddeepjyoti30%252Fytmdl%26style%3Dfor-the-badge" 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%2Fapis.deepjyoti30.dev%2Frepostatus%2Fbadge%3Frepo%3Ddeepjyoti30%252Fytmdl%26style%3Dfor-the-badge" alt="[RepoStatus](https://repostatus.deepjyoti30.dev/badge)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isn't it cool?! It supports options like &lt;code&gt;style&lt;/code&gt; of the badge where &lt;code&gt;for-the-badge&lt;/code&gt; can be used. It changes the color of the badge based on the &lt;code&gt;happiness status&lt;/code&gt; of the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  How is happiness exactly calculated
&lt;/h2&gt;

&lt;p&gt;As I mentioned earlier, the happiness of any repo depends on certain parts of the repo. Thus, once the &lt;code&gt;commit messages&lt;/code&gt;, &lt;code&gt;issue comments&lt;/code&gt; and the &lt;code&gt;PR's comments&lt;/code&gt; are extracted, they are run through a filter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering the data
&lt;/h3&gt;

&lt;p&gt;This step makes sure that the data is cleared off of any unreadable content, like an image that the user might have posted in the comments. Or some code inside a code block that the user might have added in an issue report.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the engine
&lt;/h3&gt;

&lt;p&gt;Once the data is cleared off all the unreadable content, it is passed to the &lt;code&gt;textblob&lt;/code&gt; library that runs the engine on the data.&lt;/p&gt;

&lt;p&gt;Now, this library returns a float &lt;code&gt;score&lt;/code&gt;. This score is between -1 and +1 where +1 indicates &lt;code&gt;happy&lt;/code&gt; and -1 indicates &lt;code&gt;sad&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Thus, when the engine is run on the comments, let's say we get a score of 0.8, this means, based on the comments, the repo is not &lt;code&gt;that&lt;/code&gt; happy but it's more happier as compared to being sad. So &lt;code&gt;repostatus&lt;/code&gt; considers that based on the comments the repo is &lt;code&gt;happy&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We do the above process on other aspects of the repo too, the commit messages etc.&lt;/p&gt;

&lt;p&gt;Once, all the individual scores are available, they are added up and an average is calculated. This average, mathematically, is also between &lt;code&gt;-1&lt;/code&gt; and &lt;code&gt;+1&lt;/code&gt;. This is the overall &lt;code&gt;happiness status&lt;/code&gt; of the repo.&lt;/p&gt;

&lt;p&gt;Based on this score, it is decided what color is to be assigned to the repo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching
&lt;/h3&gt;

&lt;p&gt;Since the engine takes a pretty hefty amount of memory, the score of any repo is cached for 15 days.&lt;/p&gt;

&lt;p&gt;The badges are cached for 24 hours and only after that they are updated.&lt;/p&gt;

&lt;p&gt;This caching was necessary in order to make sure the performance of the API was not effected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try &lt;a href="https://repostatus.deepjyoti30.dev" rel="noopener noreferrer"&gt;repostatus here&lt;/a&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Source
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;repostatus&lt;/code&gt; is open source. Source for the backend and the frontend can be found below&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/trotsly" rel="noopener noreferrer"&gt;
        trotsly
      &lt;/a&gt; / &lt;a href="https://github.com/trotsly/repostatus" rel="noopener noreferrer"&gt;
        repostatus
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Get Happiness status of your repo
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/trotsly/repostatus.github/logo_large.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftrotsly%2Frepostatus.github%2Flogo_large.png" alt="repostatus logo"&gt;&lt;/a&gt;
&lt;br&gt;
&lt;br&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;i&gt;Get Happiness status of your repo&lt;/i&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/09b93eaf0803089422483ebbe92b9ab502d500053d397ee2e2b81e506c209c67/68747470733a2f2f617069732e646565706a796f746933302e6465762f7265706f7374617475732f62616467653f7265706f3d74726f74736c792532467265706f737461747573267374796c653d666f722d7468652d6261646765"&gt;&lt;img src="https://camo.githubusercontent.com/09b93eaf0803089422483ebbe92b9ab502d500053d397ee2e2b81e506c209c67/68747470733a2f2f617069732e646565706a796f746933302e6465762f7265706f7374617475732f62616467653f7265706f3d74726f74736c792532467265706f737461747573267374796c653d666f722d7468652d6261646765" alt="Status of repostatus"&gt;&lt;/a&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;p&gt;Backend for &lt;a href="https://repostatus.deepjyoti30.dev" rel="nofollow noopener noreferrer"&gt;repostatus&lt;/a&gt;. Repostatus lets you calculate the happiness status of your repository.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What we do?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;We consider various parts of the repo like commit messages, comments on issues, pulls etc and run a sentiment analysis engine on the data in order to find out the happiens status.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Setup&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;You'll need to setup an environ variable named &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; that will contain an access token. In order to get the token, follow &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token" rel="noopener noreferrer"&gt;this&lt;/a&gt; article and accordingly save it to the environment.&lt;/p&gt;
&lt;p&gt;One way to save something to environment is:&lt;/p&gt;
&lt;div class="highlight highlight-source-python notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s1"&gt;os&lt;/span&gt; &lt;span class="pl-k"&gt;import&lt;/span&gt; &lt;span class="pl-s1"&gt;environ&lt;/span&gt;
&lt;span class="pl-s1"&gt;environ&lt;/span&gt;.&lt;span class="pl-en"&gt;set&lt;/span&gt;(&lt;span class="pl-s"&gt;'GITHUB_TOKEN'&lt;/span&gt;, &lt;span class="pl-s"&gt;'&amp;lt;your_token&amp;gt;'&lt;/span&gt;)&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Otherwise, it can also be set through the rc file, i:e &lt;code&gt;zshrc, bashrc etc&lt;/code&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Tests&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;For the tests, we are using &lt;a href="https://github.com/pytest-dev/pytest" rel="noopener noreferrer"&gt;pytest&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you wish to run the tests yourself, make sure you have it installed. The tests can be run by the…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/trotsly/repostatus" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I created my own blog management system. Here's how I did it.</title>
      <dc:creator>Deepjyoti Barman</dc:creator>
      <pubDate>Tue, 17 Nov 2020 16:28:14 +0000</pubDate>
      <link>https://dev.to/deepjyoti30/i-created-my-own-blog-management-system-here-s-how-i-did-it-jo5</link>
      <guid>https://dev.to/deepjyoti30/i-created-my-own-blog-management-system-here-s-how-i-did-it-jo5</guid>
      <description>&lt;p&gt;I have had a personal blog for a while now. I had written a few posts there but it was far from perfect. It was built using basic HTML, CSS and JS. I had seen all this awesome sites with unique designs and I thought, why not create my own?&lt;/p&gt;

&lt;p&gt;I went with a front-end for back-end approach which means the back-end needed to be robust in order for the content to load properly and fast.&lt;/p&gt;

&lt;p&gt;I built my API using FastAPI for Python and the webapp using VueJS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building the API
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Structering the API
&lt;/h2&gt;

&lt;p&gt;I laid down the endpoints that will possibly be needed in order for the blog to work properly.&lt;/p&gt;

&lt;p&gt;Here are some of them&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;/posts: Allow GET, POST, UPDATE, DELETE where only GET will be public and other methods would need a secret token in order to access them&lt;/li&gt;
&lt;li&gt;/subscribe: Allow POST, DELETE where only POST will be public.&lt;/li&gt;
&lt;li&gt;/related: Allow GET to get the related posts to a post.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the database, I went with mongoDB. So the idea is to store the posts as markdown in the database and let the API access it. The frontend will then just make a request and get all the data from the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing the API
&lt;/h2&gt;

&lt;p&gt;It took me a few days to get the API ready. FastAPI was really helpful with their openapi docs to provide a nice interface in order to test the API without using &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The posts and the subscribe endpoint are pretty self explanatory, here's how I created the related endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding posts related to a post
&lt;/h2&gt;

&lt;p&gt;Since all the posts will have tags linked to them, I used those to calculate a score for the post.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/related/&amp;lt;post_id&amp;gt;&lt;/code&gt; endpoint was structered to pass a &lt;code&gt;post_id&lt;/code&gt; that would tell us which post to consider the root post. Once we have this post, we can fetch all the other posts and calculate a related score.&lt;/p&gt;

&lt;p&gt;This score is calculated in the following way&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Consider root_tags are tags of the source post
# other_tags are the tags of the other post that.
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other_tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Remove duplicate tags if present
&lt;/span&gt;    &lt;span class="n"&gt;root_tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;other_tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other_tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Calculate the score now
&lt;/span&gt;    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other_tags&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above code does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intersect the root tags with the other tags set giving us the common tags&lt;/li&gt;
&lt;li&gt;Score is the division of the number of tags common between the two posts and the total number of tags present in the actual post.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way we get a score that would be between 0 and 1. Once we have this score, we can sort the posts based on the result and the posts that have a higher score are more related to a post as compared to other posts.&lt;/p&gt;

&lt;h1&gt;
  
  
  Building the webapp
&lt;/h1&gt;

&lt;p&gt;The webapp is built using VueJS. The whole idea of the frontend for backend approach is, the frontend will be dependent on the backend for the data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structuering the app
&lt;/h2&gt;

&lt;p&gt;Before building the app, I went through a few points that the app should be able to do&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It should have a home page that will show the users all the posts available&lt;/li&gt;
&lt;li&gt;It should be able to show each post&lt;/li&gt;
&lt;li&gt;It should be fast&lt;/li&gt;
&lt;li&gt;It should be able to load posts directly through a route (for example: &lt;code&gt;blog.com/nana&lt;/code&gt; should load the post &lt;code&gt;nana&lt;/code&gt; and not just the webapp that is hosted on &lt;code&gt;blog.com&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first part is pretty simple. I just used the &lt;code&gt;/posts&lt;/code&gt; route in order to get all the posts and then displayed them in a nice way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering the post dynamically
&lt;/h2&gt;

&lt;p&gt;Here's the flow of how a post is rendered&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user clicks on a post from the home page, the content of the post is passed to the router and accordingly rendered in the post view.&lt;/li&gt;
&lt;li&gt;If the post is opened using a route, the route is used to find the post and accordingly the content is shown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above basically does two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Makes the load speed faster if the user opens a post from the home page&lt;/li&gt;
&lt;li&gt;Adds the ability to load a post using the route.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;To pass the contents of the post by the route, use a route prop to pass a object that will hold the contents. In the Post view, check if this object is available or not, if not use the route to make a request and fetch the content.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What about SEO?
&lt;/h2&gt;

&lt;p&gt;Well yeah, I know SEO is important. For loading the meta tags I used &lt;a href="https://github.com/ktquez/vue-head"&gt;vue-head&lt;/a&gt; which renders the meta tags dynamically after the post is loaded using the API.&lt;/p&gt;

&lt;p&gt;This is pretty important since the meta tags are used by all the bots crawling the page. Also, Google bots are now able to crawl dynamically rendered content whcih means it should not be an issue if the tags are loaded dynamically using JS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Problems
&lt;/h1&gt;

&lt;p&gt;Except the common occurence of bugs, I did not had any problems with the back end. However there was one issue that made me question the whole idea. How do bots that do not have the ability to crawl dynamically rendered content crawl the page.&lt;/p&gt;

&lt;p&gt;For example, twitter bots crawl a page in order to show a nice card view. If the bots are not able to crawl the page then the card won't be there. Not just Twitter, a similar functionality is used by various other social share bots like the ones from Facebook and LinkedIn.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to let bots crawl the page
&lt;/h2&gt;

&lt;p&gt;Well, so how did I fix this issue? At first, I obviously thought this would be inevitable because there's no way the bots would be able to detect dynamically rendered content. One solution was to go with server side rendering but I'm better off not diving down that road.&lt;/p&gt;

&lt;p&gt;So the solution that I went with was to write a static file server in Python.&lt;/p&gt;

&lt;p&gt;What would our server do?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It should be able to return the static html, JS, CSS files.&lt;/li&gt;
&lt;li&gt;It should be able to return a rendered HTML with just the meta tags if the request is made by a bot.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built the server using Flask for Python. It detects the request making entity using the &lt;code&gt;User-Agent&lt;/code&gt; header being passed and accordingly returns an HTML. If the request is being made by a bot, it returns some HTML content that has the meta representing the post.&lt;/p&gt;

&lt;p&gt;Else it returns the proper static files.&lt;/p&gt;

&lt;p&gt;You can read about it &lt;a href="https://blog.deepjyoti30.dev/let-bots-crawl-dynamic-page"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can check my blog page &lt;a href="https://blog.deepjyoti30.dev"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post is also published in my &lt;a href="https://blog.deepjyoti30.dev/created-own-blog"&gt;personal blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>python</category>
      <category>css</category>
    </item>
  </channel>
</rss>
