<?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: MeStrak</title>
    <description>The latest articles on DEV Community by MeStrak (@mestrak).</description>
    <link>https://dev.to/mestrak</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%2F297528%2F8fec1af3-3a31-47f9-81d0-8f8e5801d1b4.jpg</url>
      <title>DEV Community: MeStrak</title>
      <link>https://dev.to/mestrak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mestrak"/>
    <language>en</language>
    <item>
      <title>Pop culture design patterns: creational</title>
      <dc:creator>MeStrak</dc:creator>
      <pubDate>Sun, 17 Oct 2021 18:28:44 +0000</pubDate>
      <link>https://dev.to/mestrak/pop-culture-design-patterns-creational-3m32</link>
      <guid>https://dev.to/mestrak/pop-culture-design-patterns-creational-3m32</guid>
      <description>&lt;p&gt;During a recent interview I asked the candidate about their understanding of design patterns. I was impressed not only with their honesty, telling me they only had basic knowledge, but also with their description of the Singleton pattern:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's like Highlander … there can be only one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since then I discovered that there are already some memes floating around with the same joke, but it made me chuckle anyway. It got me thinking that perhaps there is something in the idea of using pop culture references to remember the fundamental goals of design patterns.&lt;/p&gt;

&lt;p&gt;This series is my attempt to choose some good references. It's not to be taken too seriously, and I won't provide in depth explanations of each pattern as there are already amazing sites for this like &lt;a href="https://refactoring.guru"&gt;https://refactoring.guru&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's try creational patterns first. Here goes …&lt;/p&gt;

&lt;h1&gt;
  
  
  Singleton: Highlander - there can be only one
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8UJuolwB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebeesxz1sp1v7tpd645f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8UJuolwB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebeesxz1sp1v7tpd645f.png" alt="Highlander movie poster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image sourced from: &lt;a href="https://www.vintagemovieposters.co.uk/wp-content/uploads/2021/03/IMG_1541-scaled.jpeg"&gt;https://www.vintagemovieposters.co.uk/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Singleton ensures that only one instance of the class which implements it can exist. Global access to that object can be used within state management applications (think Redux).&lt;/p&gt;

&lt;p&gt;If you haven't seen The Highlander, you're missing out. I love it, even though it's 80s cheese. Immortals battle it out to be the only immortal, because just like the Singleton pattern 'THERE CAN BE ONLY ONE!'.&lt;/p&gt;

&lt;p&gt;Other similarities between Singleton and The Highlander:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They're both classics, and both much criticised. I'm not afraid to say that I love both of them - I can still remember my first time implementing a Singleton.&lt;/li&gt;
&lt;li&gt;In The Highlander, after beheading another immortal, all knowledge that person has obtained during their lifetime is transferred to the surviving immortal in a process called The Quickening. One of the criticisms of Singleton is that it can be used as a global store for everything, resulting in separate components of an application knowing too much about each other.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Builder: Five Guys
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XieV3ZhI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wza2d9qri3slhfvkw3hm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XieV3ZhI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wza2d9qri3slhfvkw3hm.png" alt="Five Guys burger, fries and milkshake"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image sourced from: &lt;a href="https://www.timeout.com/miami/restaurants/everything-on-the-five-guys-menu-ranked"&gt;https://www.timeout.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The goal of the Builder pattern is to construct complex objects, and allow you to create variations on those objects using the same code.&lt;/p&gt;

&lt;p&gt;A frequently used analogy for this pattern is assembling a meal in a fast food restaurant. My somewhat limited imagination allowed me to think of Five Guys. Think of making a burger as a complex task - implement the Builder pattern to call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AddPickles()
AddBacon()
AddCheese()
AddKetchup()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obviously if those are your only choices of Five Guys burger toppings something has gone wrong, but hopefully you get the idea.&lt;/p&gt;

&lt;p&gt;Builder also has an optional implementation of a Director, further abstracting the implementation and simplifying things for the client. This is like your Five Guys cashier who takes your order for a burger all the way, and a crazy milkshake with 11 mixins, and then sends all required instructions to the builder team who assemble and deliver your meal.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prototype: Futurama S6E17 - Benderama
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T4xAgUNq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/myx2pzjyi90t47synirh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T4xAgUNq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/myx2pzjyi90t47synirh.png" alt="Bender from Futurama with his clones on a triple bike"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image sourced from: &lt;a href="https://www.assignmentx.com/wp-content/uploads/2011/06/FUTURAMA-Season-6B-Benderama2.jpg"&gt;https://www.assignmentx.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The Prototype pattern is used to remove complexity from copying objects. Objects that implement the Prototype pattern know how to clone themselves, so code that needs to make a copy does not need to know the complexity of the class.&lt;/p&gt;

&lt;p&gt;In Benderama, Bender makes clones of himself. Unfortunately each clone is smaller than the previous one and when they reach molecular level they nearly destroy the world. Obviously knowing that detail this analogy falls down entirely.&lt;/p&gt;

&lt;h1&gt;
  
  
  Factory Method: The Pepsi Challenge
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OQvnfOZo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0b6se6ovocrk7zdl9k4h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OQvnfOZo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0b6se6ovocrk7zdl9k4h.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image sourced from &lt;a href="https://abcnews.go.com/Business/pepsi-challenge-returns-bubbly-twist/story?id=29552172"&gt;https://abcnews.go.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Factory Method provides a way for a class to create similar objects, but to leave the individual subclasses to use their own logic. This is done by defining an interface implemented by all subclasses so individual objects can be created using common methods, but without knowing the details of what's inside.&lt;/p&gt;

&lt;p&gt;First of all, in case you don't know what it is, the Pepsi Challenge is a marketing campaign from the 1980s where shoppers would do a blind tasting of Coke and Pepsi and say which one they preferred.&lt;/p&gt;

&lt;p&gt;Why is Factory Method similar to the Pepsi Challenge? Think of an abstract class called &lt;code&gt;SoftDrink&lt;/code&gt; which provides a creational method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CreateDrink()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And an interface implemented by every soft drink providing the following common methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FillBottle()
OpenBottle()
PourDrink()
DrinkCola()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is Factory Method! The creational logic of each drink is the secret recipe closely guarded by Coke or Pepsi. When the consumer takes the challenge, they call the &lt;code&gt;DrinkCola()&lt;/code&gt; function but they do not know if they are doing that for Coke or Pepsi, thanks to the Factory Method which hides that complexity (in the form of a big yellow box).&lt;/p&gt;

&lt;p&gt;Using this approach, Pepsi could add as many drinks as they want to into the challenge. They don't really need to do that though, as instead through shameless capitalism the two companies have successfully wiped out or bought out most other competition.&lt;/p&gt;

&lt;p&gt;One drawback of the Factory Method is that requires implementation of many subclasses. This can make quite a simple concept harder to understand than intended when actually implemented, just like my attempt to use the Pepsi Challenge to explain this pattern.&lt;/p&gt;

&lt;h1&gt;
  
  
  Abstract Factory: IKEA
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p-TAvmDS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i86et0u2jrknmdq8yskj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p-TAvmDS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i86et0u2jrknmdq8yskj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image sourced from &lt;a href="https://about.ikea.com/"&gt;https://about.ikea.com/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You may guess from the name that this one adds a layer of abstraction to the Factory Method. It is used for creating groups of related objects (a family). The Abstract Factory class holds a bunch of abstract creation methods which know how to create instances of the objects. Concrete factories will then be called to actually create the object.&lt;/p&gt;

&lt;p&gt;Think of each family like a set of matching furniture from Ikea. The furniture family might contain a chair, sofa and a table. Different style chairs would have the same chair interface, the &lt;code&gt;create()&lt;/code&gt; method would implement the style of that chair's given family. The factory for a given family will call the specific &lt;code&gt;create()&lt;/code&gt; methods to create each furniture type with the correct style matching the family.&lt;/p&gt;

&lt;p&gt;I'm sorry to say that I had a lack of imagination for this one, and the chosen analogy is the same as several other examples online. One of those is the excellently written article on &lt;a href="https://refactoring.guru/design-patterns/abstract-factory"&gt;https://refactoring.guru/&lt;/a&gt; which has a great explanation. I recommend that you spend some time reading that, and hopefully the Ikea link will start to stick.&lt;/p&gt;




&lt;p&gt;And there you have it, hopefully some memorable references for Singleton, Builder, Prototype Factory and Abstract Factory creational design patterns.&lt;/p&gt;

&lt;p&gt;Look out for further posts in this series where I will &lt;em&gt;try&lt;/em&gt; to do the same for structural and behavioural patterns.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Fin.&lt;/code&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>culture</category>
      <category>design</category>
    </item>
    <item>
      <title>Testing secure APIs by mocking JWT and JWKS</title>
      <dc:creator>MeStrak</dc:creator>
      <pubDate>Mon, 04 Jan 2021 12:12:11 +0000</pubDate>
      <link>https://dev.to/mestrak/testing-secure-apis-by-mocking-jwt-and-jwks-3g8e</link>
      <guid>https://dev.to/mestrak/testing-secure-apis-by-mocking-jwt-and-jwks-3g8e</guid>
      <description>&lt;p&gt;Recently, I've had some time to get back to my pet project &lt;a href="https://github.com/MeStrak/catkin" rel="noopener noreferrer"&gt;Catkin&lt;/a&gt;. I'm working on gradually improving the testing which was sorely neglected when I created the initial prototype app.&lt;/p&gt;

&lt;p&gt;When implementing end to end API tests I quickly ran into the issue of a missing authentication token as there is no logged-in user. As I'm using &lt;a href="https://auth0.com/" rel="noopener noreferrer"&gt;Auth0&lt;/a&gt; to keep things nice and simple for my user login implementation, I have no easy way to login a user from an endpoint directly on the backend which is the usual approach.&lt;/p&gt;

&lt;p&gt;In this article I'll explain how I solved that problem.&lt;/p&gt;

&lt;p&gt;I use the Jest for running my tests. In writing this I'm assuming that you have the basic framework up and running already so that you can run tests against your API. The full setup of jest is not covered.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Catkin user authentication process
&lt;/h1&gt;

&lt;p&gt;First let's look at how users log in to Catkin. In the Catkin login flow the following happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The application frontend connects directly to Auth0 to get a JWT token.&lt;/li&gt;
&lt;li&gt;The token is then added to the authorisation header of each request from the frontend to the backend API.&lt;/li&gt;
&lt;li&gt;Upon receiving a request, the backend validates that the token was generated by Auth0 and is valid for Catkin. This is done by the Auth0 JWKS endpoint.&lt;/li&gt;
&lt;li&gt;If the token is valid, the requested query/mutation is executed. If not, then a &lt;code&gt;401 Unauthorized&lt;/code&gt; code is returned.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Quick definitions
&lt;/h2&gt;

&lt;p&gt;Just in case you're not familiar with the terms, two fundamental things to know are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT: &lt;em&gt;JSON Web Token&lt;/em&gt; - a secure token signed by the authentication provider using a secret key. This contains the details of the authenticated user and can be used to securely store other information such as user security roles. &lt;a href="https://auth0.com/docs/tokens/json-web-tokens" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;JWKS: &lt;em&gt;JSON Web Key Set&lt;/em&gt; is a list of the public keys which can be used to verify the JWT. They are stored by the authentication provider and used in step 3 of the process described above. For Auth0 the JWKS is always found at &lt;code&gt;https://your_auth_domain.xx.auth0.com/.well-known/jwks.json&lt;/code&gt; &lt;a href="https://auth0.com/docs/tokens/json-web-tokens/json-web-key-sets" rel="noopener noreferrer"&gt;Read more&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  For the artists among you
&lt;/h1&gt;

&lt;p&gt;Here's a picture ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7lxpeoisqhlb6fmh5lk9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7lxpeoisqhlb6fmh5lk9.png" alt="Auth0 login flow"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image sourced from &lt;a href="https://auth0.com/docs/architecture-scenarios/web-app-sso/part-1" rel="noopener noreferrer"&gt;https://auth0.com/docs/architecture-scenarios/web-app-sso/part-1&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And here's another one. Simpler. Better. But you have to imagine that instead of REST it says GraphQL 😉.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdb5qov2qjkqd8wwfdfli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdb5qov2qjkqd8wwfdfli.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image sourced from &lt;a href="https://hceris.com/setting-up-auth0-with-terraform/" rel="noopener noreferrer"&gt;https://hceris.com/setting-up-auth0-with-terraform/&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With that covered, it's now time to think about how we can test our API with this additional layer of complexity.&lt;/p&gt;


&lt;h1&gt;
  
  
  Testing approach
&lt;/h1&gt;

&lt;p&gt;I need to test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;That Catkin the Catkin GraphQL API returns the correct query results/performs the expected mutation.&lt;/li&gt;
&lt;li&gt;That the security applied to the API works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the authentication flow that, is in place any unauthenticated user will be rejected. This obviously makes testing the API a little more difficult, as tests must run as an authenticated user.&lt;/p&gt;

&lt;p&gt;The two most obvious approaches to test the secured API are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connect to Auth0 during test execution to get a token.&lt;/li&gt;
&lt;li&gt;Mock a JWKS endpoint and use that for testing.(A JWKS endpoint is the thing that actually validates that the JWT is legitimate).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I would prefer to avoid option one, even though the Auth0 free tier would be enough to support my testing needs. Option two is cleaner, and my  chosen approach which I will cover below. It means that if anybody else wants to use the Catkin code they would not be tied in to using only Auth0 or having an external connection available.&lt;/p&gt;


&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;Now that we know the theory and have decided the approach, let's have a go at implementing it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Mocking the JWT and JWKS
&lt;/h2&gt;

&lt;p&gt;To fully mock the authentication process, we need to achieve the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a JWT without depending on Auth0.&lt;/li&gt;
&lt;li&gt;Allow the backend to verify the JWT without connecting to Auth0.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To do both things, we can use a lovely little library called &lt;a href="https://github.com/Levino/mock-jwks" rel="noopener noreferrer"&gt;mock-jwks&lt;/a&gt; which was created for exactly this use case.&lt;/p&gt;

&lt;p&gt;Mock-jwks works by intercepting calls to Auth0 (or actually any OAuth service) using &lt;a href="https://github.com/nock/nock" rel="noopener noreferrer"&gt;nock&lt;/a&gt;. Nock helps us to perform isolated testing of modules which make HTTP requests by intercepting those requests before they are sent to the external service and allowing us to act on them. Once the request to the JWKS endpoint has been intercepted, mock-jwks can then validate (or not) the JWT which is being passed to it.&lt;/p&gt;

&lt;p&gt;First, install the libraries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add mock-jwks nock &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in our tests we can create a mock Auth0 endpoint with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jwks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createJWKSMock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://catkin-dev.eu.auth0.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then generate a token as below. For the Auth0 token you should specify the reserved claims audience (&lt;code&gt;aud&lt;/code&gt;) and issuer (&lt;code&gt;iss&lt;/code&gt;) as you have set up in your environment variables. The &lt;code&gt;https://catkin.dev/permissions&lt;/code&gt; is specific to Catkin and an example of how you can use custom data in Auth0 which will be added to your token:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://catkin.dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://catkin-dev.eu.auth0.com/`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://catkin.dev/permissions&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="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The token can then be added to any request header:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Creates an item when user is logged in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHttpServer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// add the token to the request header&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validAuthToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;operationName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;createItemQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createItem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now whenever your backend tries to check something with Auth0, mock-jwks will intercept the request using nock, and do the check instead. No external connection is required.&lt;/p&gt;

&lt;p&gt;Likewise, we can also test that our endpoint rejects unauthenticated users by omitting the &lt;code&gt;Authorization&lt;/code&gt; header:&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Throws an error when API is called with no token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHttpServer&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/graphql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;// send the request without the auth token&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CREATE_ITEM_GQL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;createItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&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="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&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;Finally, at the end of the tests, or if we want to break the auth service for further testing, simply stop the JWKS server.&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;jwks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cleaning up the code
&lt;/h2&gt;

&lt;p&gt;The basic test is now in place but the implementation is a bit messy. To help with re-use of the code, let's implement a helper file which contains all code for setting up the JWKS mock, generating tokens, etc. Auth service settings should also not be hard-coded; they will instead be passed to this helper function allowing us to provide incorrect details in the token to simulate an invalid token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/MeStrak/catkin/blob/main/backend/test/auth.helpers.ts" rel="noopener noreferrer"&gt;&lt;strong&gt;auth.helper.ts&lt;/strong&gt;&lt;/a&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createJWKSMock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;JWKSMock&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mock-jwks&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startAuthServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwksServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;JWKSMock&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;jwks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createJWKSMock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwksServer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JWKSMock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;authAudience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;string&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;token&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;aud&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authAudience&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/userinfo`&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://catkin.dev/permissions&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="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&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="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;testprovider|12345678&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stopAuthServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JWKSMock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;jwks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&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;These functions are then called from my global setup.ts file &lt;code&gt;beforeAll()&lt;/code&gt; and &lt;code&gt;afterAll&lt;/code&gt; functions, providing a global JWKS endpoint and JWT that can easily be reused in all tests. Take a look at the full setup here: &lt;a href="https://github.com/MeStrak/catkin" rel="noopener noreferrer"&gt;https://github.com/MeStrak/catkin&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Wrap up
&lt;/h1&gt;

&lt;p&gt;As the objective of Catkin is to provide a hosted environment for several organisations, the security must be rock solid. Thanks to mock-jwks it was straightforward to mock the whole authentication process allowing the API to be fully tested, including fail cases for unauthenticated users.&lt;/p&gt;

&lt;p&gt;I now have a simple framework in place allowing me to quickly write tests simulating authenticated or unauthenticated users.&lt;/p&gt;

&lt;p&gt;The next step will be to simulate authenticating as users with different roles to check that granular security levels work correctly.&lt;/p&gt;




&lt;p&gt;Thanks for reading! I deliberately kept this fairly brief to provide an overview. I hope that even at this high level the article is still useful. As always, I'm happy to answer any questions you may have.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Fin.&lt;/code&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>security</category>
      <category>graphql</category>
      <category>node</category>
    </item>
    <item>
      <title>Semantic release with Python, Poetry &amp; GitHub Actions 🚀</title>
      <dc:creator>MeStrak</dc:creator>
      <pubDate>Sun, 20 Dec 2020 23:05:34 +0000</pubDate>
      <link>https://dev.to/mestrak/semantic-release-with-python-poetry-github-actions-20nn</link>
      <guid>https://dev.to/mestrak/semantic-release-with-python-poetry-github-actions-20nn</guid>
      <description>&lt;p&gt;I'm planning to add a few features to &lt;a href="https://github.com/MeStrak/dr-sven" rel="noopener noreferrer"&gt;Dr. Sven&lt;/a&gt; thanks to some interest from my colleagues. Before doing so, I needed to set up some proper release management as I will be introducing some breaking changes as well as automated deployment.&lt;/p&gt;

&lt;p&gt;To manage this automatic versioning, the obvious choice is semantic versioning/semantic release which automatically increments a version number based on commit conventions.&lt;/p&gt;

&lt;p&gt;Here's how ...&lt;/p&gt;

&lt;h1&gt;
  
  
  What I want to achieve
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Automatic version number increment of releases.&lt;/li&gt;
&lt;li&gt;Version numbers based on conventional commit format (Semantic Versioning).&lt;/li&gt;
&lt;li&gt;Automatically create a release object on GitHub.&lt;/li&gt;
&lt;li&gt;Only allow merging to main when tests and linting are passing.&lt;/li&gt;
&lt;li&gt;Perform a release following a merge to the main branch.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;If you don't want to read the detail you should be able to implement this in under 10 minutes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install this GitHub bot to enforce semantic PRs: &lt;a href="https://github.com/apps/semantic-pull-requests" rel="noopener noreferrer"&gt;https://github.com/apps/semantic-pull-requests&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set the same version number in &lt;code&gt;pyproject.toml&lt;/code&gt; and &lt;code&gt;__init__.py&lt;/code&gt;. For example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;./pyproject.toml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tool.poetry]
name = "your-project"
version = "0.0.1"
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;src_folder/__init__.py&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;__version__ = '0.0.1'
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add Semantic Release section to &lt;code&gt;pyproject.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tool.semantic_release]
version_variable = [
    "src_folder/__init__.py:__version__",
    "pyproject.toml:version"
]
branch = "main"
upload_to_pypi = false
upload_to_release = true
build_command = "pip install poetry &amp;amp;&amp;amp; poetry build"
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create the file &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; within your repo, and copy the contents of this file into it to set up your CI action: &lt;code&gt;https://github.com/MeStrak/dr-sven/blob/main/.github/workflows/ci.yml&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Done! Read on if you would like a little more explanation.&lt;/p&gt;




&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;p&gt;Before diving into the code, let's do a light review of the principles behind Semantic Versioning to understand how the version numbers are generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conventional Commits
&lt;/h2&gt;

&lt;p&gt;This is a specification designed to ensure that commit messages are human-readable. Developers using this convention use specific commit prefix text such as &lt;code&gt;fix:&lt;/code&gt; for commits which fix a bug, and &lt;code&gt;feat:&lt;/code&gt; for commits which add a new feature. The most common convention is the convention developed by the Angular team which includes the following keywords:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;build:&lt;/code&gt;, &lt;code&gt;chore:&lt;/code&gt;, &lt;code&gt;ci:&lt;/code&gt;, &lt;code&gt;docs:&lt;/code&gt;, &lt;code&gt;style:&lt;/code&gt;, &lt;code&gt;refactor:&lt;/code&gt;, &lt;code&gt;perf:&lt;/code&gt;, &lt;code&gt;test:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A scope can also be added in parentheses. For example: &lt;code&gt;feat(blog): add code parsing&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BREAKING CHANGE&lt;/code&gt; can also be added into the commit text to indicate that the commit is not backwards compatible.&lt;/p&gt;

&lt;p&gt;These commit conventions are then used in the Semantic Versioning specification to calculate the version number.&lt;/p&gt;

&lt;p&gt;Have a look at the full specification &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, here are two tools worth investigating to help enforce this convention in all commit messages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/commitizen/cz-cli" rel="noopener noreferrer"&gt;commitizen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/conventional-changelog/commitlint" rel="noopener noreferrer"&gt;commitlint&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Semantic Versioning
&lt;/h2&gt;

&lt;p&gt;Semantic Versioning, or SemVer is a specification defining a clear, logical version numbering system for an API. In this case, I'm actually using it to version an application with no external API, but the specification still holds very well because I have a requirement to indicate whether a new release is backwards compatible with configuration files.&lt;/p&gt;

&lt;p&gt;I would recommend reading the full specification, but here is an excellent summary taken directly from the specification:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given a version number MAJOR.MINOR.PATCH, increment the:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MAJOR version when you make incompatible API changes,&lt;/li&gt;
&lt;li&gt;MINOR version when you add functionality in a backwards compatible manner, and&lt;/li&gt;
&lt;li&gt;PATCH version when you make backwards compatible bug fixes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take a look at the full specification &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Various packages existing to calculate a SemVer version number based on conventional commit messages. We'll be using Semantic Release to do this, and perform a release at the same time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semantic Release
&lt;/h2&gt;

&lt;p&gt;Semantic Release is a tool which automatically sets a version number in your repo, tags the code with the version number and creates a release (for example publishing it to NPM, or PyPi).&lt;/p&gt;

&lt;p&gt;This is done using the contents of Conventional Commit style messages to generate a version number. For example, &lt;code&gt;fix:&lt;/code&gt; will bump the PATCH version, &lt;code&gt;feat:&lt;/code&gt; will bump the minor version, and a message containing &lt;code&gt;BREAKING CHANGE&lt;/code&gt; will bump the major version to indicate that the release is not fully backwards compatible with the previous version.&lt;/p&gt;

&lt;p&gt;The original (as far as I know) semantic-release package was created for node projects and can be found &lt;a href="https://github.com/semantic-release/semantic-release" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll be using the Python implementation of that project, &lt;a href="https://python-semantic-release.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;Python Semantic Release&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;Now that we have covered the basic concepts, let's look at how to implement them. We need to do the following things, as listed in the TL;DR section but this time with a bit more explanation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ensure Conventional Commits are used.&lt;/li&gt;
&lt;li&gt;Configure Semantic Release in pyproject.toml.&lt;/li&gt;
&lt;li&gt;Configure a basic quality CI step using GitHub Actions.&lt;/li&gt;
&lt;li&gt;Add a release step using Python Semantic Release.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Ensure Conventional Commits are used
&lt;/h2&gt;

&lt;p&gt;The use of Conventional Commits is central to our whole versioning strategy. If it's not used then nothing will work. To enforce use of this type of commit on PRs I'll be using a GitHub bot which is easy-peasy to install: &lt;a href="https://github.com/apps/semantic-pull-requests" rel="noopener noreferrer"&gt;https://github.com/apps/semantic-pull-requests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just install that bot for your GitHub project, and you'll automatically see a new check for any new PR created on your project. If the PR doesn't contain commits which comply with the Conventional Commit specification then the check will fail.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7ton1noh6d6vbr5g9ba7.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7ton1noh6d6vbr5g9ba7.PNG" alt="Semantic Pull Request pending"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a later enhancement, I will add a pre-commit hook to the repo to check the format of local commit messages because it can be pretty annoying for developers to discover issues like this only when a PR is raised.&lt;/p&gt;

&lt;p&gt;The first step is done! Easy right?&lt;/p&gt;
&lt;h2&gt;
  
  
  Configure Semantic Release in pyproject.toml
&lt;/h2&gt;

&lt;p&gt;Semantic Release can be configured in &lt;code&gt;pyproject.toml&lt;/code&gt; according to your requirements. The main things to note here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As we're using Poetry the version variable is managed in both &lt;code&gt;__init.py__&lt;/code&gt; and &lt;code&gt;pyproject.toml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Branch is set to main, as we're using the GitHub flow branching strategy. We want our releases to be performed using code on the main branch, which is where all code is merged to for production deployment. If you use a different strategy just change the name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;upload_to_pypi&lt;/code&gt; has been set to false because Dr. Sven is not published on PyPi, so this is irrelevant for me.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[tool.semantic_release]
version_variable = [
    "dr_sven/__init__.py:__version__",
    "pyproject.toml:version"
]
branch = "main"
upload_to_pypi = false
upload_to_release = true
build_command = "pip install poetry &amp;amp;&amp;amp; poetry build"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Configure a basic quality CI step using GitHub Actions
&lt;/h2&gt;

&lt;p&gt;If you're reading this, I assume you're already aware that GitHub Actions is GitHub's CICD workflow solution. Workflows are configured in YAML files stored in the &lt;code&gt;.github/workflows&lt;/code&gt; folder in your repo.&lt;/p&gt;

&lt;p&gt;I'm using the nice and simple &lt;a href="https://guides.github.com/introduction/flow/" rel="noopener noreferrer"&gt;GitHub flow&lt;/a&gt; branching strategy, where all bugfixes and features have their own separate branch, and when complete each branch is merged to main and deployed.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3dxtg7nk4g6et0qv3b4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3dxtg7nk4g6et0qv3b4i.png" alt="GitHub, flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Dr. Sven CI pipeline will consist of 2 main stages, known as jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quality&lt;/strong&gt;: runs linting and automated tests on the code, preventing merging if any of these steps fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release&lt;/strong&gt;: runs following the successful quality job when code has been merged to the main branch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full workflow file can be found &lt;a href="https://raw.githubusercontent.com/MeStrak/dr-sven/main/.github/workflows/ci.yml" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Although the structure and syntax are simple, let's break it down to make sure we understand each part.&lt;/p&gt;

&lt;p&gt;The header defines that the CI workflow will run whenever there is a direct push to main, or a pull request to merge another branch to main.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: CI
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quality job will use an ubuntu runner instance. The runner is the name of the application which runs GitHub Actions jobs, such as unit tests in almost the same way as you would run them on your development PC.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;checkout&lt;/code&gt; action will checkout the code so that it is available for you to use in the executing runner.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setup-python&lt;/code&gt; will install Python on the runner.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  Quality:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-python@v2
      with:
        python-version: 3.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we install Poetry, check the version (not required but sometimes useful to see in the logs), and then install the dependencies using Poetry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - name: Install Python Poetry
      uses: abatilo/actions-poetry@v2.1.0
      with:
        poetry-version: 1.1.2
    - name: Configure poetry
      shell: bash
      run: python -m poetry config virtualenvs.in-project true
    - name: View poetry version
      run: poetry --version
    - name: Install dependencies
      run: |
        python -m poetry install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, run Flake8 linting and pytest unit tests on the Poetry virtualenv. Other linting or unit test frameworks could also be used here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - name: Lint with flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        python -m poetry run flake8 . --exclude .venv --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        python -m poetry run flake8 . --exclude .venv --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Test with pytest
      run: |
        python -m poetry run python -m pytest -v tests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the quality step ready, simple 🤓.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a release step using Python Semantic Release
&lt;/h2&gt;

&lt;p&gt;Now that our tests have run and passed the quality stage, it's time to release our new version.&lt;/p&gt;

&lt;p&gt;The release job will only run after a successful &lt;code&gt;quality&lt;/code&gt; stage. We also check the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a push to main. This prevents a release from being performed on PRs before they are merged.&lt;/li&gt;
&lt;li&gt;The commit message doesn't contain &lt;code&gt;chore(release)&lt;/code&gt;, the message used by Semantic Release when a release is performed. Without this we would have an endless loop of releases, each release triggered by the previous release.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Release:
    needs: Quality
    # https://github.community/t/how-do-i-specify-job-dependency-running-in-another-workflow/16482
    if: github.event_name == 'push' &amp;amp;&amp;amp; github.ref == 'refs/heads/main' &amp;amp;&amp;amp; !contains(github.event.head_commit.message, 'chore(release):')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install Python on the runner, checkout the code, then install and run &lt;code&gt;python-semantic-release publish&lt;/code&gt;, which will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bump the version number in both version files.&lt;/li&gt;
&lt;li&gt;Tag the code with that version.&lt;/li&gt;
&lt;li&gt;Create a GitHub release object.&lt;/li&gt;
&lt;li&gt;Commit the updated files to the main branch.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@v2
        with:
          python-version: 3.8
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Semantic Release
        run: |
          pip install python-semantic-release
          git config user.name github-actions
          git config user.email github-actions@github.com
          semantic-release publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Following this simple step, our releases are now created and the workflow is complete. It could even be simplified further by using the &lt;a href="https://python-semantic-release.readthedocs.io/en/latest/automatic-releases/github-actions.html" rel="noopener noreferrer"&gt;Python Semantic Release GitHub Action&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final result
&lt;/h1&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;We can now see all Actions workflow runs from the &lt;a href="https://github.com/MeStrak/dr-sven/actions/" rel="noopener noreferrer"&gt;GitHub actions page&lt;/a&gt;. Looking at a specific run we can see the successful quality and release steps:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzeqpphl7wm7mavnewhxc.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzeqpphl7wm7mavnewhxc.PNG" alt="Successful workflow diagram in GitHub actions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A release has been created, and assets can be downloaded for that specific version:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi619y5qunm3lck2ot74o.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi619y5qunm3lck2ot74o.PNG" alt="Release version shown in GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course some tweaks can always be made to improve the workflow, but now we have a nice lightweight CI workflow in place to support automated testing and release as I develop Dr. Sven further.&lt;/p&gt;

&lt;h1&gt;
  
  
  Possible Improvements
&lt;/h1&gt;

&lt;p&gt;As GitLab say: &lt;a href="https://about.gitlab.com/handbook/values/#everything-is-in-draft" rel="noopener noreferrer"&gt;&lt;em&gt;everything is in draft&lt;/em&gt;&lt;/a&gt;. In keeping with that, of course there are things that I can and will improve in this CI workflow. Here are some of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lock the main branch&lt;/strong&gt;: currently I can commit directly to the main branch, which is bad practice. As I'm currently the only project contributor it's OK for now, but I need to lock the branch and configure Semantic Release to use a specific token with permission to push to that branch when updating the version numbers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CommitLint within the CI&lt;/strong&gt;: currently I've used the Semantic Commit bot which is great to allow you to see that your PR contains compliant commits from the PR page. However, technically, I could bypass this and commit something without compliant commit messages directly to main, skipping the release. This can be prevented by adding a CommitLint step within the pipeline (also locking the main branch will help).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integration testing within the CI&lt;/strong&gt;: currently only unit tests are executed. With this testing strategy, all integration testing must be performed manually on my dev machine or in AWS. Not very &lt;strong&gt;DevOpsy&lt;/strong&gt;, only really acceptable in my simple, single developer environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pre-commit hook to ensure Conventional Commits are used&lt;/strong&gt;: prevent any commits from being made unless the developer is using the specified commit style. This prevents developer frustration by avoiding the annoying discovery at PR time that you didn't follow the project conventions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading! Please leave a comment, I'm happy to answer any questions you may have.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Fin.&lt;/code&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>github</category>
    </item>
    <item>
      <title>Guidebook: fostering open source in a large corporate environment (part 2 of 2)</title>
      <dc:creator>MeStrak</dc:creator>
      <pubDate>Sun, 20 Dec 2020 13:03:08 +0000</pubDate>
      <link>https://dev.to/mestrak/guidebook-fostering-open-source-in-a-large-corporate-environment-part-2-of-2-5efg</link>
      <guid>https://dev.to/mestrak/guidebook-fostering-open-source-in-a-large-corporate-environment-part-2-of-2-5efg</guid>
      <description>&lt;p&gt;In &lt;a href="https://mestrak.com/blog/guidebook-fostering-open-source-in-a-large-corporate-environment-part-1-2i22"&gt;part one&lt;/a&gt; we looked at some of the simple steps that you can take to help your enterprise support open source projects. Now let's look at some options to give more by investing company resources.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sponsoring an open source project
&lt;/h1&gt;

&lt;p&gt;Depending on your company, this one may be even easier than contributing to code, support and documentation. Many open source projects ask for sponsorship to help the core team dedicate more time to working on the project. There are now several platforms available for you to easily sponsor the projects including GitHub Sponsors and Open Collective.&lt;/p&gt;

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

&lt;p&gt;Typically, projects who are serious about sponsorship offer several levels, from basic to advanced with different advantages. Quite often these are called Gold, Silver and Bronze level memberships.&lt;/p&gt;

&lt;p&gt;An example from one of my favourite projects, NestJS &lt;a href="https://nestjs.com/:"&gt;https://nestjs.com/:&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backers ($2 USD per month):&lt;/strong&gt; Support us with a monthly donation and help us continue our activities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bronze ($100 USD per month):&lt;/strong&gt; Become a bronze sponsor and get your logo on our README on GitHub as well as a small logo on the official page with a link to your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silver ($250 USD per month):&lt;/strong&gt; Become a silver sponsor and get your logo on our README on GitHub as well as on the official page with a link to your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gold $500 USD per month:&lt;/strong&gt; Become a gold sponsor and get your logo on our README on GitHub as well as on the official page and the official documentation with a link to your site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base ($1000 USD per month):&lt;/strong&gt; Become a base sponsor and get your logo on our README on GitHub as well as on the official page and the official documentation with a link to your site (as a main project sponsor).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some projects also offer priority support for higher level sponsorships, and some even have a specific corporate support model, for example Vuetify (another one of my favourites!) &lt;a href="https://vuetifyjs.com/en/introduction/enterprise/"&gt;https://vuetifyjs.com/en/introduction/enterprise/&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make your motivation clear
&lt;/h2&gt;

&lt;p&gt;My personal motivation for sponsoring projects, is that I want things to be 'fair'. I believe that if large companies are making use of open source software, it shouldn't be completely free. They have the means, and therefore a social responsibility (as well as a mutual interest) to help that project continue to evolve.&lt;/p&gt;

&lt;p&gt;Whenever I suggest sponsoring a new project, I'm very transparent about my personal motivation, but I'm also careful to put together a well-structured case about why it is the right business decision. Depending on whom you discuss this with, each of these points will be more or less important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose your projects strategically
&lt;/h2&gt;

&lt;p&gt;We touched on this in part one - you might not be in a position to offer sponsorship to many projects. Establishing a standard tech stack certainly helps with this by limiting the number of open source projects you use, allowing you to focus sponsorship on only a few. Here are some other things you might consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How hard will it be to switch to another technology if the project is no longer maintained?&lt;/li&gt;
&lt;li&gt;How well-supported is the project by other sponsors? If it already has heavy investment then perhaps your sponsorship will count more elsewhere&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Invest time to educate your finance/procurement teams about open source, and the sponsorship model
&lt;/h2&gt;

&lt;p&gt;Your procurement team is likely to be highly trained in the art of negotiation. The reality is that this skill isn't usually required in the sponsorship model - you can decide what you pay and in most cases (unless there is a premium support model) it doesn't make any difference to the final cost. Often the sponsorship cost of the project is also relatively low compared to the type of licence agreement or service contract that negotiations are usually designed for. Make sure you spend time with your contacts in this team to explain the philosophy and make sure they are on-board with your idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Financials
&lt;/h2&gt;

&lt;p&gt;Not everybody will have the same motivation, and this is a very important part to prepare properly. As I mentioned you will most likely need to discuss this with finance or procurement colleagues used to an entirely different licensing model. The idea of paying what you want may be an alien concept, and you need to show them why it is a good idea. Also, if this is the start of your sponsorship journey, it's likely that you'll want to sponsor additional projects in the future.&lt;/p&gt;

&lt;p&gt;Do your calculations including the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ongoing costs of continued sponsorship.&lt;/li&gt;
&lt;li&gt;Estimate how much you would pay in licence fees and maintenance costs if you were purchasing a commercial product to do the same thing. Often there isn't actually an equivalent commercial product. In that case just get whatever costs you can for alternative options if you chose to buy instead of build your project.&lt;/li&gt;
&lt;li&gt;List of benefits (improved support, project continuity, in some cases publicity, ...).&lt;/li&gt;
&lt;li&gt;Compare sponsorship cost to developer daily rate, and estimate how much development cost time is saved by using the open source solution.&lt;/li&gt;
&lt;li&gt;List of future expenditure as you sponsor additional projects.&lt;/li&gt;
&lt;li&gt;Have everything projected as monthly and yearly costs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Be patient
&lt;/h3&gt;

&lt;p&gt;You may, from time to time, become frustrated with this process of trying to get financial approval for something which seems low cost. Don't let this de-motivate you. Remind yourself that what you're doing is for the good of the project community and your company, as well as being personally rewarding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make a big thing about the community support
&lt;/h2&gt;

&lt;p&gt;Assuming you followed my advice about choosing well-supported projects with an active community for the most critical components of your tech stack, you should be very confident in advertising this fact when discussing with procurement teams. That active open source community is essentially replacing a paid maintenance and support plan. Again, as with licence fees be ready with estimated savings for maintenance and support agreements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logo or not?
&lt;/h2&gt;

&lt;p&gt;Often procurement teams will have a clause built into the contract about whether a vendor can use your company logo on their website to show that you are a customer. The example sponsorship model I showed focuses heavily on showing your company logo in different places of the project documentation depending on how much you decide to sponsor them.&lt;/p&gt;

&lt;p&gt;If your company logo is shown, it's a great feeling of pride, and can be a badge of honour for the developers using that technology.&lt;/p&gt;

&lt;p&gt;If you're not sure what your company rules are about showing the logo, don't let it delay your sponsorship - just ask the project owners not to display your logo as you still have some things to check internally.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to pay
&lt;/h2&gt;

&lt;p&gt;This one may seem frustrating, but if you've made it this far you're nearly there. This is the final red tape to cut through!&lt;/p&gt;

&lt;p&gt;GitHub sponsors and Open Collective are wired up to take payment via credit card or PayPal. Some large companies don't like paying in this way, accepting only payments by PO - in this case reach out to the project owner and ask them to create an invoice. If your company only allows you to purchase from a list of pre-approved vendors they probably have a partner that you can use who will handle payments like this for a small fee.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cherry on the cake
&lt;/h2&gt;

&lt;p&gt;Congratulations! You've successfully navigated the internal red tape, educated your colleagues and have just sponsored your first open source project. If you've made a significant contribution, why not reach out to the project sponsor and ask them to record a short video to say thanks to your team? Hopefully they are grateful for your support and will be happy to do this for you. Trust me - this is a great way to announce the sponsorship internally and will be a great motivation to your developers.&lt;/p&gt;

&lt;h1&gt;
  
  
  Launching an open source project
&lt;/h1&gt;

&lt;p&gt;If you have an internal project which you think could be useful for others, then this option is for you. It generally requires the highest effort, but you can start with a small library if you think that you have something which is useful to share. You'll be publicly releasing something under your company name and depending on the company there are likely a few official stamps that you'll need before releasing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  List the benefits
&lt;/h2&gt;

&lt;p&gt;Start by making a bullet pointed list of the benefits of releasing your project. You may or may not present this as a slide to people involved in the approval. You should do it even if you won't present it, because it helps you to ensure you have a succinct explanation of what you aim to do and why. Here are some which come to mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Motivate the team by working together on a project visible to the outside world.&lt;/li&gt;
&lt;li&gt;Attract talent when recruiting thanks to your visible open source activities.&lt;/li&gt;
&lt;li&gt;Great publicity for your company.&lt;/li&gt;
&lt;li&gt;Depending on your project making it open source may help to improve architecture because of the thought process required to ensure the design will be useful for the community (I had this experience with whispr: &lt;a href="https://github.com/Sanofi-IADC/whispr/"&gt;https://github.com/Sanofi-IADC/whispr/&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Licence review by legal team
&lt;/h2&gt;

&lt;p&gt;Larger corporations most likely have a legal team who should be involved in the approval process for releasing a project. Check if your contact has experience with open source software. If not, spend some time explaining the concept to them and why you want to launch a project. Once you have educated them in the open source philosophy, have them review and approve the specific open source licence that you intend to use to release your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communications/marketing team
&lt;/h2&gt;

&lt;p&gt;You might also have an internal communications or marketing team. Again, spend some time with them to educate them about open source and explain your project. You might need their blessing to release a project stamped with your company name, and if all goes well you might be able to get their help with publicising the launch of your project either internally or externally.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrap-up
&lt;/h1&gt;

&lt;p&gt;That's it! In the first part of this series I tried to give you some suggestions on how to get started with open source in your enterprise. In this second part we looked at how to go about sponsoring an open source project, and the importance of educating your non techy colleagues to help you do this. Finally we looked at launching your own project. Large or small I know that you and your team will find this incredibly satisfying.&lt;/p&gt;

&lt;p&gt;I hope that this series has given you some ideas and inspiration for promoting open source within your enterprise. Thanks for reading!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Guidebook: fostering open source in a large corporate environment (part 1 of 2)</title>
      <dc:creator>MeStrak</dc:creator>
      <pubDate>Sun, 11 Oct 2020 11:48:29 +0000</pubDate>
      <link>https://dev.to/mestrak/guidebook-fostering-open-source-in-a-large-corporate-environment-part-1-2i22</link>
      <guid>https://dev.to/mestrak/guidebook-fostering-open-source-in-a-large-corporate-environment-part-1-2i22</guid>
      <description>&lt;p&gt;In this series I'll present a guide on how to help your company use open source software responsibly by fully investing in it and contributing to the open source community. I believe that all companies using open source for software development should be responsible for this.&lt;/p&gt;

&lt;p&gt;I write this following my very positive personal and professional experience in launching a significant open source initiative within a large enterprise and I hope to inspire others to do the same.&lt;/p&gt;

&lt;p&gt;You should read on if …&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you work in a large company and want to educate non technical colleagues about open source&lt;/li&gt;
&lt;li&gt;you want to promote the use of open source in your company&lt;/li&gt;
&lt;li&gt;you want to help your company contribute to the open source community&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;p&gt;We'll look at the following topics. In each one I'll suggest how you can deal with some challenges you may experience in a larger corporate environment which is not familiar with the open source philosophy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Why use open source?:&lt;/strong&gt; a quick overview of the benefits of using open source. Useful for your elevator pitch when you need to explain it internally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source licensing:&lt;/strong&gt; licensing basics you should be able to explain clearly to your colleagues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choosing an open source tech stack:&lt;/strong&gt; some simple things to consider when choosing your stack.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contributing to the community:&lt;/strong&gt; a look at the easiest way to contribute to open source projects. This is the easiest and fastest way to provide support without worrying about internal approval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://mestrak.com/blog/guidebook-fostering-open-source-in-a-large-corporate-environment-part-2-of-2-5efg"&gt;&lt;strong&gt;Part 2:&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sponsoring open source projects:&lt;/strong&gt; sponsoring a project financially to help main contributors dedicate more time to the project, or to help with general project expenses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launching an open source project:&lt;/strong&gt; creating your own open source project by releasing something that has been developed internally under an open source licence.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Introduction to Open Source
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why use open source?
&lt;/h3&gt;

&lt;p&gt;With a quick Google search you'll find a plethora of articles professing the goodness of open source. Here are a few of the benefits that appeal the most to me, and which speak clearly within a corporate environment.&lt;/p&gt;

&lt;h4&gt;
  
  
  Better together
&lt;/h4&gt;

&lt;p&gt;Thanks to the open sourcing of some amazing projects such as Express.js, Vue.js and Go to name only a few, all developers have free access to some amazing tools. Instead of writing functionality from scratch they can accelerate development by building on something great which is already open source.&lt;/p&gt;

&lt;p&gt;Working this way results in better tools available for all as they are improved together by a community. It also promotes inclusivity as the cost barrier is removed for accessing these tools and frameworks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Community security
&lt;/h4&gt;

&lt;p&gt;All the source code is available to anyone who wants to look at it, including security flaws in the code and vulnerable dependencies. While at first sight this may seem like a security risk, it is actually very beneficial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code is visible to everyone, meaning it can be reviewed and issues fixed by many people.&lt;/li&gt;
&lt;li&gt;Thanks to these community peer reviews, open source projects must fix their vulnerabilities. There can be no hidden or ignored vulnerabilities, something which is possible in closed source software.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Empowered developers
&lt;/h4&gt;

&lt;p&gt;You'll see this as a common theme throughout this article. Fully embracing open source and encouraging your developers to contribute to the project will be a great motivation for your development team. They have the chance to work on not only your project, but also to make a difference to something bigger. This applies to developers on external contracts too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Open source licences
&lt;/h3&gt;

&lt;p&gt;You need to know about this because if you want to promote open source development within your organisation, you may need to educate others about different licence types.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When choosing a licence for a new project it's really important to read the detail to know which one suits your project best.&lt;/li&gt;
&lt;li&gt;When using open source code you must check the licence to make sure it's compatible with your intended use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many licencing models are available for open source projects, which impact how the code can be used and modified by others. Well known licences include MIT, Mozilla Public Licence, BSD and GNU GPLv3. Licences are generally categorised as 'permissive' or 'copyleft', depending on how the source code can be used and whether those modifying the code for their own use must make the changes visible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Permissive: allows a great deal of freedom with no obligation to contribute code back to the community, or to share modified code. The most well known highly permissive licence is probably the MIT licence.&lt;/li&gt;
&lt;li&gt;Copyleft: obligates people improving or adapting the code in an open source project to share their improvements with the community so they can be integrated back into the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I will explain, reviewing the licence is also an important part of choosing your tech stack. There are also tools available to scan your code within to check open source licence compliance. This can be very useful to demonstrate to internal security and legal teams that you're compliant.&lt;/p&gt;

&lt;p&gt;Choosing a licence is also a very important part of creating an open source project, because depending on the success of the project it can make a big difference to how people use and contribute to it. In cases where somebody intends to modify your open source code to make a proprietary product, the licence could be a deciding factor in whether to use your code.&lt;/p&gt;

&lt;p&gt;More about the different licence types here: &lt;a href="https://choosealicense.com/"&gt;https://choosealicense.com/&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Choosing an open source tech stack
&lt;/h2&gt;

&lt;p&gt;When choosing a tech stack, developers and architects have a huge number of open source projects to choose from. Here are a few tips to help you do this in a controlled way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Standardise your tech stack to focus sponsorship
&lt;/h3&gt;

&lt;p&gt;It's not uncommon for major parts of a tech stack to be based on developer preference. With multiple product teams working in parallel this can lead to many similar but different frameworks being used in your landscape. Assuming you intend to sponsor key parts of your stack, you should aim to standardise those similar parts. For example where you use a Node.JS backend, aim to use the same framework across products, the same goes for your UI components. Doing this will allow you to focus your sponsorship on fewer technologies and give a meaningful contribution to these projects. It also facilitates cross-collaboration between teams as developers will be familiar with the stack used on multiple products.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mitigate the risk for critical components
&lt;/h3&gt;

&lt;p&gt;You must be careful when choosing technologies for critical components in your stack. You can either make sure you choose a well-supported open source project ensuring continued development and maintenance from the community, or  acknowledge and accept the risk if a project is less active but provides a unique feature that you would like to take advantage of.&lt;/p&gt;

&lt;p&gt;Some of the metrics that you can use to measure an open source project's success:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issue turnover: too many open issues can signify a project which is not well maintained, but if no issues are opened at all this may be an indicator that very few people are using the project &lt;/li&gt;
&lt;li&gt;Number of weekly downloads: package repositories such as NPM give a simple insight into the frequency that open source packages are downloaded&lt;/li&gt;
&lt;li&gt;Contributions to master: Similar to issue turnover, check how frequently commits are made to master (by people, not by bots) - this is a good indication of whether a project is actively developed or not&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Check the licence is compatible with your intended use
&lt;/h3&gt;

&lt;p&gt;If you plan to make modifications to the code in parts of your tech stack which you would be uncomfortable sharing with the rest of the world, you should be looking for permissive licenses such as MIT or BSD. If you make changes to the core code on a GNU licensed project for example, you must open source those changes under the same licence. In most cases you'll probably find that there's no issue, but check anyway!&lt;/p&gt;




&lt;h2&gt;
  
  
  Contributing back to the community
&lt;/h2&gt;

&lt;p&gt;If you got this far, you're probably convinced about the value of open source, both the technological and philosophical aspects. Now that you have your open source tech stack in place, let's look at the easiest way for you to contribute back to the open source world.&lt;/p&gt;

&lt;p&gt;Many open source projects have only a few core contributors. These contributors keep the project alive. They do all major development, documentation and maintenance. The simplest and easiest way that you can contribute to open source is to help some of the projects in your tech stack with these activities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation updates - just do it!
&lt;/h3&gt;

&lt;p&gt;It's easy to get hung up on corporate approval for things like this but in reality this isn't always necessary. If someone from your team finds a mistake in some documentation, it's not necessary to advertise that the pull request you're raising is on behalf of your company. Most likely the developer already has a GitHub account and there's no reason not to just raise a PR for the updated documentation. You'll need the documentation internally anyway, so the only overhead is a few minutes of preparing a well formatted pull request according to the project guidelines. Have a developer update the documentation, and it's done. The dev will feel empowered, you'll feel great having made the first small step on your corporate open source journey, and you've helped out the project core team who have developed this amazing piece of technology for you, for free!&lt;/p&gt;

&lt;h3&gt;
  
  
  Helping out with support requests
&lt;/h3&gt;

&lt;p&gt;Many open source projects run support either through GitHub issues or a free chat tool like Discord. Your developers are working with the project every day and undoubtedly have valuable knowledge they can share with others. Every time your team ask for help, or file a bug report they're essentially taking advantage of a free maintenance agreement where a member of the community (frequently a core team member) will help you out.&lt;/p&gt;

&lt;p&gt;This is another very simple one - ask your dev team to browse the issues channel of one of the projects you're using a couple of times a week. Let them know they are free to answer questions raised by other project users. You're not trying to create a fully fledged support team, you're contributing to a community. Imagine what would happen if everybody did the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bug fixes and enhancements
&lt;/h3&gt;

&lt;p&gt;This is the next step and does require a little more effort from your development team. You'll find most open source communities very welcoming of anyone submitting useful PRs for their project. Your developers will feel great having contributed a fix, and will improve their knowledge of the technology by developing on its codebase. My recommendation to start on this one is when an issue is identified by your team:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask your developers to review it together and see if they have an idea of how it can be resolved&lt;/li&gt;
&lt;li&gt;You can then raise an issue through GitHub (or whatever the official bugs channel of the project is), and suggest a fix for that issue, making it clear you would like to work on it.&lt;/li&gt;
&lt;li&gt;Assuming the proposed approached is accepted, then go ahead and fix it!&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;In this first article we've looked at some arguments for using open source within large enterprises, considerations when choosing an an open source tech stack and how to take your first steps into contributing to an open source project. I've tried to make as many suggestions as I can to keep it simple and avoid corporate red tape. Make sure you follow your company's internal rules, but don't be afraid to challenge them if you need to - sometimes it's the only way to make progress.&lt;/p&gt;

&lt;p&gt;In part two we'll look at how you can use this information to educate colleagues within your company about open source to help you sponsor some open source projects, and potentially release your own company open source project.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Introducing Dr. Sven: open source data health checker</title>
      <dc:creator>MeStrak</dc:creator>
      <pubDate>Wed, 19 Aug 2020 13:51:10 +0000</pubDate>
      <link>https://dev.to/mestrak/introducing-dr-sven-14m9</link>
      <guid>https://dev.to/mestrak/introducing-dr-sven-14m9</guid>
      <description>&lt;p&gt;I just created a new project on GitHub that I want to share with you all: &lt;a href="https://github.com/MeStrak/dr-sven"&gt;https://github.com/MeStrak/dr-sven&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What follows is a brief description of Dr. Sven adapted from the project readme, and a demo of what it can do.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Dr. Sven?
&lt;/h1&gt;

&lt;p&gt;Dr Sven is a very simple data health checker which performs a checkup on your data to give an indication of whether it's in good shape or not.&lt;/p&gt;

&lt;p&gt;The doctor comes in the form of an AWS Lambda function written in Python. It scans a table in an AWS Athena database based on a set of simple data rules defined by you to check that the data is in good shape. When I say simple I mean it - right now the only rule that exists is a check that every day within a given time range contains at least the specified number of records. Specific dates or days can be ignored to avoid spam.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dr. Sven practises KISS medicine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No the doc doesn't break the hypocratic oath, it just prescribes to the ideology that simple is better.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Motivation for creating Dr. Sven
&lt;/h1&gt;

&lt;p&gt;Dr. Sven is designed with a data lake type situation in mind where data is transferred from one data source to another repository. Most data ingestion monitoring tools I've seen focus on whether individual processes have run without error at the expected time. In some complex scenarios they also try  to check that elements of the source data match the finally processed data which can be very difficult to do.&lt;/p&gt;

&lt;p&gt;Imagine this situation ...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;all dashboards are green 😄&lt;/li&gt;
&lt;li&gt;logs contain no errors 😁&lt;/li&gt;
&lt;li&gt;all appears well with the world 🚀&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... then a pesky human looks at the data and says&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;"but there are no records from Sunday, that's not right"&lt;/em&gt; 😰&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;"there's something weird - it looks like there's a hole in this data from 1 month ago"&lt;/em&gt; 😱.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project aims to reduce dependence on that human by providing a naive yet effective way to detect data issues. Specify some data rules defining what you expect to see, then let our kind doctor check if the processed data meets the criteria.&lt;/p&gt;

&lt;h1&gt;
  
  
  Demo!
&lt;/h1&gt;

&lt;p&gt;Let's take a look at a simple example of using Dr. Sven using the AWS Athena getting started dataset. We'll create a checkup for the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check every weekend contains at least a few records&lt;/li&gt;
&lt;li&gt;Check that every week day contains lots of records&lt;/li&gt;
&lt;li&gt;Specify a few dates to ignore for each of those rules, because we already know that there are some exceptions to the rule&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites if you want to play along
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Installation instructions are in the project readme, and it's better to leave them there for now as I improve them. Here I'll show what Dr. Sven can do once installed.&lt;/li&gt;
&lt;li&gt;We'll be using the dataset in the AWS Athena getting started guide: &lt;a href="https://docs.aws.amazon.com/athena/latest/ug/getting-started.html"&gt;https://docs.aws.amazon.com/athena/latest/ug/getting-started.html&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuring Dr. Sven
&lt;/h2&gt;

&lt;p&gt;Each dataset you want to check must have a specific configuration file. A configuration file can contain multiple rules as long as they are for the same dataset. &lt;/p&gt;

&lt;p&gt;First let's look at the configuration file (this example is contained in the project on GitHub), then I'll explain the parts which aren't immediately obvious.&lt;/p&gt;

&lt;h3&gt;
  
  
  Datasource.query
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This is the basis of the dataset which will be inspected

&lt;ul&gt;
&lt;li&gt;It must return data with 2 columns: date, and count, where count is the number of records on that day&lt;/li&gt;
&lt;li&gt;The query text must contain &lt;strong&gt;'{start_date}'&lt;/strong&gt; and &lt;strong&gt;'{end_date}'&lt;/strong&gt; which will be replaced by Dr. Sven based on the date range you specify.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Date must be in the formay YYYY-mm-dd&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rules
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;This is the list of the rules which will be executed on the data&lt;/li&gt;
&lt;li&gt;Currently only &lt;code&gt;[[rules.min_records]]&lt;/code&gt; is implemented, which checks that the data contains the required number of records&lt;/li&gt;
&lt;li&gt;Ignore dates is a list of dates which should be excluded from the rule, usually because there is a good reason why the data doesn't on that day meet the requirements of the rule&lt;/li&gt;
&lt;li&gt;Ignore days is a list of days, using the name of the day which should be excluded. For example if every Sunday there is system maintenance so there will be no records, add 'Sunday' to this list. You can also use the friendly names 'Weekends' instead of ['Saturday', 'Sunday'] and 'Weekdays' instead of ['Monday', 'Tuesday', ..., 'Friday']&lt;/li&gt;
&lt;li&gt;In this configuration we exclude 'Weekdays' to define a rule only for the weekend, and 'Weekends' to define a rule only applicable Monday - Friday
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[general]&lt;/span&gt;
&lt;span class="py"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Example config for Dr. Sven using AWS Athena getting started dataset"&lt;/span&gt;
&lt;span class="py"&gt;output_location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"lovely-bucket"&lt;/span&gt;
&lt;span class="py"&gt;output_region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"eu-west-1"&lt;/span&gt;

&lt;span class="nn"&gt;[datasource]&lt;/span&gt;
&lt;span class="py"&gt;query&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SELECT date, COUNT(*) as count FROM cloudfront_logs WHERE date BETWEEN date '{start_date}' AND date '{end_date}' GROUP BY date"&lt;/span&gt;
&lt;span class="py"&gt;database&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"mydatabase"&lt;/span&gt;
&lt;span class="py"&gt;start_date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2014-07-01&lt;/span&gt;
&lt;span class="py"&gt;end_date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2014-08-08&lt;/span&gt;

&lt;span class="nn"&gt;[rules]&lt;/span&gt;
  &lt;span class="nn"&gt;[[rules.min_records]]&lt;/span&gt;
  &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Weekends must have a few records"&lt;/span&gt;
  &lt;span class="py"&gt;ignore_dates&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2014-07-12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2014-07-13&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="py"&gt;ignore_days&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["Weekdays"]&lt;/span&gt;
  &lt;span class="py"&gt;min_records&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="py"&gt;explanation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"The logs should always have some activity at weekends, just not as much as in the week"&lt;/span&gt;

  &lt;span class="nn"&gt;[[rules.min_records]]&lt;/span&gt;
  &lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Weekdays must have many records"&lt;/span&gt;
  &lt;span class="py"&gt;ignore_dates&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2014-07-06&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2014-07-07&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2014-07-06&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2014-07-25&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="py"&gt;ignore_days&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;["Weekends"]&lt;/span&gt;
  &lt;span class="py"&gt;min_records&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="py"&gt;explanation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"There will always be a lot of log activity during the week unless there is some downtime"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running Dr. Sven
&lt;/h2&gt;

&lt;p&gt;Now we launch our dr-sven lambda with this cloudwatch event (replacing bucket name with the bucket you put the config file in specified).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"s3"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-bucket"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aws-athena-example.toml"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Checking the results
&lt;/h2&gt;

&lt;p&gt;Dr. Sven will output two files with the timestamp, checkup ID and the &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Summary: .md file containing a summary of the results for each rule (number of rows checked, passed, failed and ignored)&lt;/li&gt;
&lt;li&gt;Results: .csv file with a line per date for each failed rule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dataset that we tested actually only contains data for two dates. We configured Dr. Sven to check data over from 2014-07-01 to 2014-08-08, so we're expecting a lot of failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary.md
&lt;/h3&gt;

&lt;p&gt;Contains a summary of all rules that were checked, importantly telling you how many rules failed which you can then check in results.csv.&lt;/p&gt;

&lt;h4&gt;
  
  
  Weekends must have a few records
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;The logs should always have some activity at weekends, just not as much as in the week&lt;/strong&gt;&lt;br&gt;
Total dates checked: 39&lt;br&gt;
Ignored: 31&lt;br&gt;
Passed: 1&lt;br&gt;
Failed: 7&lt;/p&gt;

&lt;h4&gt;
  
  
  Weekdays must have many records
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;There will always be a lot of log activity during the week unless there is some downtime&lt;/strong&gt;&lt;br&gt;
Total dates checked: 39&lt;br&gt;
Ignored: 12&lt;br&gt;
Passed: 1&lt;br&gt;
Failed: 26&lt;/p&gt;

&lt;h3&gt;
  
  
  Results.csv
&lt;/h3&gt;

&lt;p&gt;The results .csv output lists every issue found, along with the name of the rule taken from the config file. I've truncated the output below because it was too long - the real output found 32 issues, and correctly excluded ignored dates and days.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;count&lt;/th&gt;
&lt;th&gt;day_of_week&lt;/th&gt;
&lt;th&gt;symptom&lt;/th&gt;
&lt;th&gt;failed_rule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;06/07/2014&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Sunday&lt;/td&gt;
&lt;td&gt;Expected at least 10 records but found 0&lt;/td&gt;
&lt;td&gt;Weekends must have a few records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19/07/2014&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Saturday&lt;/td&gt;
&lt;td&gt;Expected at least 10 records but found 0&lt;/td&gt;
&lt;td&gt;Weekends must have a few records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20/07/2014&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Sunday&lt;/td&gt;
&lt;td&gt;Expected at least 10 records but found 0&lt;/td&gt;
&lt;td&gt;Weekends must have a few records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(…)&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;04/08/2014&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Monday&lt;/td&gt;
&lt;td&gt;Expected at least 100 records but found 0&lt;/td&gt;
&lt;td&gt;Weekdays must have many records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;06/08/2014&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Wednesday&lt;/td&gt;
&lt;td&gt;Expected at least 100 records but found 0&lt;/td&gt;
&lt;td&gt;Weekdays must have many records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;07/08/2014&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Thursday&lt;/td&gt;
&lt;td&gt;Expected at least 100 records but found 0&lt;/td&gt;
&lt;td&gt;Weekdays must have many records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;08/08/2014&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Friday&lt;/td&gt;
&lt;td&gt;Expected at least 100 records but found 0&lt;/td&gt;
&lt;td&gt;Weekdays must have many records&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Wrap-up
&lt;/h1&gt;

&lt;p&gt;So there you have it. Like I said Dr. Sven is a really simple way to check that your data obeys some basic rules. I don't know if it's useful to others but I'm very eager to hear your feedback!&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>python</category>
      <category>database</category>
    </item>
    <item>
      <title>Ongoing notes on things I did to get WSL2 to work</title>
      <dc:creator>MeStrak</dc:creator>
      <pubDate>Tue, 14 Jul 2020 11:55:45 +0000</pubDate>
      <link>https://dev.to/mestrak/ongoing-notes-on-things-i-did-to-get-wsl2-to-work-4bmj</link>
      <guid>https://dev.to/mestrak/ongoing-notes-on-things-i-did-to-get-wsl2-to-work-4bmj</guid>
      <description>&lt;p&gt;In the interests of productivity, and many issues trying to get dev stuff to work on my Windows workstation I started to use WSL 2 (I skipped WSL 1).&lt;/p&gt;

&lt;p&gt;This post will be used for ongoing notes of things that I had to fix during the setup. It might not be complete as I'm starting my notes after the fact but I'll do my best to remember everything.&lt;/p&gt;

&lt;p&gt;All the fixes are actually simple, but hopefully it will still be useful to save some time for people facing the same issues.&lt;/p&gt;




&lt;h1&gt;
  
  
  NPM/Yarn install not working
&lt;/h1&gt;

&lt;p&gt;Unfortunately I don't have a log of the exact error messages from this time, but when trying to install these package managers I got an error message (something related to not being able to connect - I couldn't even ping anything external).&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix 🚀
&lt;/h2&gt;

&lt;p&gt;DNS settings had not been configured, even pinging external sites didn't work.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a file: /etc/wsl.conf.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Put the following lines in the file in order to ensure the your DNS changes do not get blown away&lt;br&gt;
&lt;code&gt;[network]&lt;br&gt;
generateResolvConf = false&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In a cmd window, run &lt;code&gt;wsl --shutdown&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restart WSL2&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a file: &lt;code&gt;/etc/resolv.conf&lt;/code&gt;. If it exists, delete and replace existing one with this new file (&lt;code&gt;sudo rm /etc/resolv.conf&lt;/code&gt; to delete, and if you get a warning about an existing swap file then &lt;code&gt;D&lt;/code&gt; to delete it).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Put the following line in the file&lt;br&gt;
&lt;code&gt;nameserver 8.8.8.8&lt;/code&gt; (or use your DNS server instead of 8.8.8.8 which is a Google DNS server)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In a cmd window, run &lt;code&gt;wsl --shutdown&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Restart WSL2&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Credit: this fix is copied directly from this &lt;a href="https://gist.github.com/coltenkrauter/608cfe02319ce60facd76373249b8ca6"&gt;gist&lt;/a&gt; by @coltenkrauter&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  error: linker &lt;code&gt;cc&lt;/code&gt; not found when compiling rust code
&lt;/h1&gt;

&lt;p&gt;When running rustc I came across the following error, basically because gcc wasn't installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rustc fart.rs
error: linker `cc` not found
  |
  = note: No such file or directory (os error 2)

error: aborting due to previous error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The fix 🚀
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt update
$ sudo apt install build-essential
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;I did actually have more errors than this which I've written up separately, see the 404 not found error below&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Package installation fails with 404 errors
&lt;/h1&gt;

&lt;p&gt;Annoying errors like the one below to show when I was installing some new packages (actually after trying to install build-essential).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt install build-essential
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  cpp cpp-9 dpkg-dev fakeroot g++ g++-9 gcc gcc-9 gcc-9-base libalgorithm-diff-perl libalgorithm-diff-xs-perl
  libalgorithm-merge-perl libasan5 libatomic1 libc-dev-bin libc6-dev libcc1-0 libcrypt-dev libdpkg-perl libfakeroot
  libfile-fcntllock-perl libgcc-9-dev libisl22 libitm1 liblsan0 libmpc3 libquadmath0 libstdc++-9-dev libtsan0
  libubsan1 linux-libc-dev make
Suggested packages:
  cpp-doc gcc-9-locales debian-keyring g++-multilib g++-9-multilib gcc-9-doc gcc-multilib autoconf automake libtool
  flex bison gdb gcc-doc gcc-9-multilib glibc-doc bzr libstdc++-9-doc make-doc
The following NEW packages will be installed:
  build-essential cpp cpp-9 dpkg-dev fakeroot g++ g++-9 gcc gcc-9 gcc-9-base libalgorithm-diff-perl
  libalgorithm-diff-xs-perl libalgorithm-merge-perl libasan5 libatomic1 libc-dev-bin libc6-dev libcc1-0 libcrypt-dev
  libdpkg-perl libfakeroot libfile-fcntllock-perl libgcc-9-dev libisl22 libitm1 liblsan0 libmpc3 libquadmath0
  libstdc++-9-dev libtsan0 libubsan1 linux-libc-dev make
0 upgraded, 33 newly installed, 0 to remove and 33 not upgraded.
Need to get 1096 kB/35.2 MB of archives.
After this operation, 157 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
Ign:1 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 linux-libc-dev amd64 5.4.0-31.35
Err:1 http://security.ubuntu.com/ubuntu focal-updates/main amd64 linux-libc-dev amd64 5.4.0-31.35
  404  Not Found [IP: 91.189.88.142 80]
E: Failed to fetch http://security.ubuntu.com/ubuntu/pool/main/l/linux/linux-libc-dev_5.4.0-31.35_amd64.deb  404  Not Found [IP: 91.189.88.142 80]
E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important bit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Failed to fetch http://security.ubuntu.com/ubuntu/pool/main/l/linux/linux-libc-dev_5.4.0-31.35_amd64.deb  404  Not Found [IP: 91.189.88.142 80]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running with &lt;code&gt;--fix-missing&lt;/code&gt; as suggested in the error message and a few blog posts gave the same error.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo apt-get update&lt;/code&gt; didn't work either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo apt-get update --fix-missing &amp;amp;&amp;amp; apt-get install gcc
Hit:1 https://dl.yarnpkg.com/debian stable InRelease
Hit:2 http://archive.ubuntu.com/ubuntu focal InRelease
Hit:3 https://deb.nodesource.com/node_12.x focal InRelease
Get:4 http://security.ubuntu.com/ubuntu focal-security InRelease [107 kB]
Get:5 http://archive.ubuntu.com/ubuntu focal-updates InRelease [111 kB]
Get:6 http://archive.ubuntu.com/ubuntu focal-backports InRelease [98.3 kB]
Reading package lists... Done
E: Release file for http://security.ubuntu.com/ubuntu/dists/focal-security/InRelease is not valid yet (invalid for another 10d 6h 3min 42s). Updates for this repository will not be applied.
E: Release file for http://archive.ubuntu.com/ubuntu/dists/focal-updates/InRelease is not valid yet (invalid for another 10d 6h 3min 50s). Updates for this repository will not be applied.
E: Release file for http://archive.ubuntu.com/ubuntu/dists/focal-backports/InRelease is not valid yet (invalid for another 10d 6h 4min 14s). Updates for this repository will not be applied.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The fix 🚀
&lt;/h2&gt;

&lt;p&gt;The issue was that my clock was out of sync with the Windows host (I don't know why).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ sudo hwclock --hctosys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this &lt;code&gt;$ sudo apt update&lt;/code&gt; ran happily, as did &lt;code&gt;$ sudo apt install build-essential&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Credit: I found this solution &lt;a href="https://askubuntu.com/questions/1096930/sudo-apt-update-error-release-file-is-not-yet-valid"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Can't add python poetry auto completions for bash
&lt;/h1&gt;

&lt;p&gt;Got the error &lt;code&gt;bash: /etc/bash_completion.d/poetry.bash-completion: Permission denied&lt;/code&gt; when running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ poetry completions bash &amp;gt; /etc/bash_completion.d/poetry.bash-completion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the time of writing (10 Aug 2020), according to the output of &lt;code&gt;$ poetry help completions&lt;/code&gt; this should be the command to install bash completions for poetry.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix 🚀
&lt;/h2&gt;

&lt;p&gt;Run this command instead!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ poetry completions bash | sudo tee /etc/bash_completion.d/poetry.bash-completion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Credit: I found this solution &lt;a href="https://github.com/python-poetry/poetry/issues/1017"&gt;here&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>windows</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
