<?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: Geoffroy Empain</title>
    <description>The latest articles on DEV Community by Geoffroy Empain (@gempain).</description>
    <link>https://dev.to/gempain</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%2F425400%2Fe6912645-5e7c-4c15-997b-e45bb3a9446d.jpg</url>
      <title>DEV Community: Geoffroy Empain</title>
      <link>https://dev.to/gempain</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gempain"/>
    <language>en</language>
    <item>
      <title>Shipping Jamstack like a hero - beware, it's cool</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Mon, 25 Jan 2021 16:23:20 +0000</pubDate>
      <link>https://dev.to/gempain/shipping-jamstack-like-a-hero-beware-it-s-cool-2a3f</link>
      <guid>https://dev.to/gempain/shipping-jamstack-like-a-hero-beware-it-s-cool-2a3f</guid>
      <description>&lt;p&gt;So you love React, Angular, Vue, Gatsby and other Jamstack frameworks ? Hooray, I do too 🎉 &lt;/p&gt;

&lt;p&gt;But.... we've all had these issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How do I deploy ? With Netlify, Docker, or a good old HTTP server ? 🤔&lt;/li&gt;
&lt;li&gt;When I need to change my API URL... I have to rebuild and re-deploy 😞&lt;/li&gt;
&lt;li&gt;When I need to preview a small change... re-deploying takes forever 😞&lt;/li&gt;
&lt;li&gt;Managing SSL certificates is a pain. It should be automatic when I ship a new site 😡&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The solution: &lt;a href="https://github.com/getmeli/meli"&gt;Meli&lt;/a&gt;, a self-hosted platform built on top of the well-known &lt;a href="https://github.com/caddyserver/caddy"&gt;Caddy Server&lt;/a&gt;. Out of the box, you get automatic HTTPs, zero-downtime, and heavy-duty performance.&lt;/p&gt;

&lt;p&gt;I've installed Meli on my VPS, so I'll skip this part which takes about 2 minutes with Docker Compose, but checkout &lt;a href="https://docs.meli.sh/get-started/installation"&gt;the docs&lt;/a&gt; for instructions. From here, I'll assume you've installed Meli at &lt;code&gt;https://meli.company.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Yl48NyKk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tzj5ts3lrp2vmfsbyy1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yl48NyKk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tzj5ts3lrp2vmfsbyy1y.png" alt="Meli login scren"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying a site to Meli
&lt;/h2&gt;

&lt;p&gt;Let's start with a very simple site, &lt;code&gt;dist/index.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Meli example Vue.js app&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
Hello !
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once logged in to your Meli instance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a site named &lt;code&gt;hello&lt;/code&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D2e1vSZQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ssge8x05d8snqqjre0vd.gif" alt="Create a site in Meli"&gt; &lt;/li&gt;
&lt;li&gt;get your site ID
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--brFMNEkn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vayv9ksga18ffn93u6uj.gif" alt="Get your site ID in Meli"&gt;
&lt;/li&gt;
&lt;li&gt;get your upload token
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aI1-Ibcg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/b296yrvsely46lj0q65b.gif" alt="Get your site upload token in Meli"&gt; &lt;/li&gt;
&lt;li&gt;upload your site
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @getmeli/cli upload &lt;span class="se"&gt;\&lt;/span&gt;
   ./dist &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:80 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--site&lt;/span&gt; &lt;span class="s2"&gt;"8f30f74f-4b63-4dc3-b8dc-788ca43740a8"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &amp;lt;site-token&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--branch&lt;/span&gt; &lt;span class="s2"&gt;"latest"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your site is now available at &lt;code&gt;https://hello.meli.company.com&lt;/code&gt; 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ncVrp7hu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ze7zn5ugh9uej7sihj70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ncVrp7hu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ze7zn5ugh9uej7sihj70.png" alt="Example site in meli"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Serve your site at &lt;a href="https://hello.com"&gt;https://hello.com&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Okay, I'll agree, &lt;a href="https://hello.meli.company.com"&gt;https://hello.meli.company.com&lt;/a&gt; isn't really sexy. We want our awesome site to be served at &lt;code&gt;https://hello.com&lt;/code&gt;. To do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In your Meli site, configure a custom domain with &lt;code&gt;hello.com&lt;/code&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IYIELHka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/901nr9xxaw7zr74h8z2o.gif" alt="Configure custom domain"&gt; &lt;/li&gt;
&lt;li&gt;In the DNS zone of &lt;code&gt;hello.com&lt;/code&gt;, add an &lt;code&gt;A&lt;/code&gt; record that points the IP of &lt;code&gt;meli.company.com&lt;/code&gt;. For subdomains like &lt;code&gt;sub.hello.com&lt;/code&gt;, you can use a &lt;code&gt;CNAME&lt;/code&gt; record that points to &lt;code&gt;hello.meli.domain.com&lt;/code&gt; &lt;strong&gt;only&lt;/strong&gt; when it is the &lt;strong&gt;single&lt;/strong&gt; record (all types combined) configured for that subdomain.&lt;/li&gt;
&lt;li&gt;Browse &lt;a href="https://hello.com"&gt;https://hello.com&lt;/a&gt;, and off you go !&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Deploying to Meli from your CI
&lt;/h2&gt;

&lt;p&gt;You can also automatically deploy when you push to your Git repository. For example, with Github Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;push&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;12'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;publish"&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npx @getmeli/cli@next upload \&lt;/span&gt;
            &lt;span class="s"&gt;./dist \&lt;/span&gt;
            &lt;span class="s"&gt;--url "https://meli.domain.com" \&lt;/span&gt;
            &lt;span class="s"&gt;--site "&amp;lt;your-site-id&amp;gt;" \&lt;/span&gt;
            &lt;span class="s"&gt;--token "$MELI_TOKEN"&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MELI_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MELI_TOKEN }}&lt;/span&gt;
          &lt;span class="c1"&gt;# using default GITHUB_TOKEN set by Github Actions&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;code&gt;--branch&lt;/code&gt; is not used here, it is auto-detected by the Meli CLI.&lt;/p&gt;

&lt;p&gt;With this setup, you'll get pull request previews in Github:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uG3ccLyn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ncbu5osjdcw2ktegk3s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uG3ccLyn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2ncbu5osjdcw2ktegk3s.png" alt="Screen Shot 2021-01-25 at 15.40.50"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Runtime environment variables in your static site
&lt;/h2&gt;

&lt;p&gt;Meli allows you to override any path in your site with dynamically generated content, per branch.&lt;/p&gt;

&lt;p&gt;Let's see how this works with a basic Vue app. We'll replace our &lt;code&gt;dist/index.html&lt;/code&gt; and upload it to our &lt;code&gt;hello&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Meli example Vue.js app&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ error }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"env"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;{{ env.name }}&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&lt;/span&gt;{{JSON.stringify(env, null, 2)}}&lt;span class="nt"&gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/env.json&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="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we're fetching &lt;code&gt;/env.json&lt;/code&gt;. We'll configure this file in Meli using the UI. For now, let's upload our site to our &lt;code&gt;latest&lt;/code&gt; branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @getmeli/cli upload &lt;span class="se"&gt;\&lt;/span&gt;
   ./dist &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:80 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--site&lt;/span&gt; &lt;span class="s2"&gt;"8f30f74f-4b63-4dc3-b8dc-788ca43740a8"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &amp;lt;site-token&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--branch&lt;/span&gt; &lt;span class="s2"&gt;"latest"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This branch will be available at &lt;code&gt;https://hello.meli.domain.com&lt;/code&gt;. Now let's upload it to a new branch named &lt;code&gt;demo&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @getmeli/cli upload &lt;span class="se"&gt;\&lt;/span&gt;
   ./dist &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:80 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--site&lt;/span&gt; &lt;span class="s2"&gt;"8f30f74f-4b63-4dc3-b8dc-788ca43740a8"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt; &amp;lt;site-token&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--branch&lt;/span&gt; &lt;span class="s2"&gt;"demo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This branch will be served at &lt;code&gt;https://demo.hello.meli.domain.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's configure &lt;code&gt;/env.json&lt;/code&gt; in Meli:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;under branch &lt;code&gt;latest&lt;/code&gt;, add a file redirect for path &lt;code&gt;/env.json&lt;/code&gt; and with content &lt;code&gt;{"name": "prod"}&lt;/code&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LK05YVcN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/irp3c844lcswxgzngnvz.gif" alt="Add file redirect to latest branch"&gt; &lt;/li&gt;
&lt;li&gt;in under branch &lt;code&gt;demo&lt;/code&gt;, add a file redirect for path &lt;code&gt;/env.json&lt;/code&gt; and with content &lt;code&gt;{"name": "demo"}&lt;/code&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--njqnX0Mz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gu4f79lu5jl5tlop8mln.gif" alt="Create env redirect for demo branch"&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, when you go to &lt;code&gt;https://hello.meli.domain.com&lt;/code&gt;, you see this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OV9kdRgk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p2kctdr10xjyupfo9d34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OV9kdRgk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/p2kctdr10xjyupfo9d34.png" alt="Environment preview of main branch in Meli demo site"&gt;&lt;/a&gt;&lt;br&gt;
and &lt;code&gt;https://demo.hello.meli.domain.com&lt;/code&gt; shows:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mQibmqeN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l03jwhcanwrb77cobrdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mQibmqeN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/l03jwhcanwrb77cobrdr.png" alt="Screen Shot 2021-01-25 at 14.45.24"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Meli is a really cool project which allows you to self-host your static sites and frontend apps. It features a lot of cool things, and more are to come.&lt;/p&gt;

&lt;p&gt;Further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.meli.sh/"&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/getmeli/meli"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/getmeli"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>angular</category>
      <category>vue</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Meli, a Netlify-like platform for deploying static sites</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Fri, 11 Dec 2020 16:05:24 +0000</pubDate>
      <link>https://dev.to/gempain/meli-a-netlify-like-platform-for-deploying-static-sites-2m36</link>
      <guid>https://dev.to/gempain/meli-a-netlify-like-platform-for-deploying-static-sites-2m36</guid>
      <description>&lt;p&gt;Hi there 👋&lt;/p&gt;

&lt;p&gt;We used to host our sites on Netlify, but our eyes fill with glitter when we hear open source and self-hosted 🎉. So, we built Meli, which essentially is a Netlify alternative that lets you deploy static sites and frontend applications with ease, featuring per-branch deployments, web/slack/mattermost/email hooks, an API, and a way to manage organizations, teams and sites easily.&lt;/p&gt;

&lt;p&gt;We built Meli on top of &lt;a href="https://github.com/caddyserver/caddy"&gt;Caddy&lt;/a&gt;, a very powerful HTTP server. We've used Typescript, Node (backend), React (frontend) and MongoDB for the database.&lt;/p&gt;

&lt;p&gt;It's a beta, but you can install super easily with Docker Compose: &lt;a href="https://docs.meli.sh/get-started/installation"&gt;https://docs.meli.sh/get-started/installation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check us out at &lt;a href="https://github.com/getmeli/meli"&gt;https://github.com/getmeli/meli&lt;/a&gt; 🚀&lt;/p&gt;

&lt;p&gt;Looking forward for your feedback 😀&lt;/p&gt;

</description>
      <category>devops</category>
      <category>react</category>
      <category>angular</category>
    </item>
    <item>
      <title>I wrote this app to practice my React, Node, Mongo, Redis and Docker skills</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Wed, 04 Nov 2020 11:39:07 +0000</pubDate>
      <link>https://dev.to/gempain/i-wrote-this-app-to-practice-my-react-node-mongo-redis-and-docker-skills-4bep</link>
      <guid>https://dev.to/gempain/i-wrote-this-app-to-practice-my-react-node-mongo-redis-and-docker-skills-4bep</guid>
      <description>&lt;p&gt;Hi there 👋&lt;/p&gt;

&lt;p&gt;Last week, as I was struggling to connect to my &lt;a href="https://Gitlab.com"&gt;Gitlab.com&lt;/a&gt; account, I noticed that their &lt;a href="https://status.gitlab.com/"&gt;status page&lt;/a&gt; was mentioning &lt;strong&gt;operational&lt;/strong&gt; while in reality they were down - they later reported the incident.&lt;/p&gt;

&lt;p&gt;That gave me the idea (spoiler alert, it's not something new) to create a very simple, painless website monitoring app where I could just add endpoints easily and get notified when something went down. Sure, I could use &lt;a href="https://prometheus.io/"&gt;Prometheus&lt;/a&gt; and &lt;a href="https://grafana.com/"&gt;Grafana&lt;/a&gt;, but it's a hassle to manage when you've got several projects to monitor - and for most projects, I honestly don't look at CPU usage etc, I just want to know if they're up or down, so deploying the whole stack is unnecessary. Then you've got things like &lt;a href="https://updown.io"&gt;UpDown&lt;/a&gt; and &lt;a href="https://uptimerobot.com/"&gt;Uptime Robot&lt;/a&gt; - and I'm certainly missing many others - but where's the fun when I have spare time and could practice my React, NodeJS, and MongoDB skills ?&lt;/p&gt;

&lt;p&gt;I also wanted to experiment with &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt; which I've heard so much about, configured in so many &lt;code&gt;docker-compose.yml&lt;/code&gt; files but never actually used in code. I've also wanted to use Stripe's API and see how I could integrate billing in my apps for a while, so I figured this would be a good occasion - and I have to tell you, it is by far the most well designed, documented and sdk-complete API I have ever used (disclaimer, I don't work for Stripe, I just think it's important to recognize when something's truly great).&lt;/p&gt;

&lt;p&gt;My requirements were quite simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple way to add endpoints and customize request method and headers&lt;/li&gt;
&lt;li&gt;simple way to configure notifications (email, Slack, Mattermost...)&lt;/li&gt;
&lt;li&gt;a way to check my endpoints from different places in the world&lt;/li&gt;
&lt;li&gt;fine tune the amount of down time before getting a notification&lt;/li&gt;
&lt;li&gt;have a nice status page with cool - but meaningful - graphs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also needed a cool name, so I went with "Probe" - I found the idea of having probes around the world  a good match.&lt;/p&gt;

&lt;p&gt;I published the app this week at &lt;a href="https://probe.sh"&gt;https://probe.sh&lt;/a&gt; and would love to know what you think ! I was thinking about open sourcing it on Github as &lt;a href="https://github.com/probe-sh/probe"&gt;probe-sh/probe&lt;/a&gt; so anyone could self-host and help making it better - what do you think ? What license should I use ?&lt;/p&gt;

&lt;p&gt;I've only deployed probes to France and Canada for now, but if you want other locations, additional notification types or features, let me know on Twitter &lt;a href="https://twitter.com/probe_sh"&gt;@probe_sh&lt;/a&gt; or on &lt;a href="https://github.com/probe-sh/probe"&gt;Github&lt;/a&gt; 🚀. I'm already planning to add a public status page for endpoints and a way to group them - stay tuned !&lt;/p&gt;

&lt;p&gt;Looking forward to your feedback  🎉&lt;/p&gt;

</description>
      <category>devops</category>
      <category>react</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Looking for beta users !</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Mon, 02 Nov 2020 10:55:17 +0000</pubDate>
      <link>https://dev.to/gempain/looking-for-beta-users-9a3</link>
      <guid>https://dev.to/gempain/looking-for-beta-users-9a3</guid>
      <description>&lt;p&gt;Today we launched Pmbot 2.0.0 beta, and are looking for beta users that would like to try it out.&lt;/p&gt;

&lt;p&gt;It's a complete re-write and setup is now just one click !&lt;/p&gt;

&lt;p&gt;Blog article &lt;a href="https://blog.pmbot.io/introducing-pmbot-v2-0-0-beta/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;New cloud platform at &lt;a href="https://cloud.pmbot.io"&gt;cloud.pmbot.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Looking forward to your feedback !&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gOZ9drrw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5fpt5xw8riubi0wj0zvt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gOZ9drrw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5fpt5xw8riubi0wj0zvt.png" alt="Screen Shot 2020-11-01 at 13.02.48"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pbCLRswD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y94el0zb5zt2m44lp362.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pbCLRswD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y94el0zb5zt2m44lp362.png" alt="Screen Shot 2020-11-01 at 13.01.13"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fhebRNQC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fg27jg629ro3gxt0gmgj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fhebRNQC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fg27jg629ro3gxt0gmgj.png" alt="Screen Shot 2020-11-01 at 13.01.21"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wcz0zVWQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9otndsmig4o1mt18id4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wcz0zVWQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/9otndsmig4o1mt18id4q.png" alt="Screen Shot 2020-11-01 at 13.01.28"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XF_gaIk2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j7tov9evywvj1dw47cxt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XF_gaIk2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/j7tov9evywvj1dw47cxt.png" alt="Screen Shot 2020-11-01 at 13.01.37"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yfhg85GK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/57i2uctbnlhk01ycozzd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yfhg85GK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/57i2uctbnlhk01ycozzd.png" alt="Screen Shot 2020-11-01 at 13.01.47"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2sb9KxOu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mneacv22qvrbqgbzkba2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2sb9KxOu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mneacv22qvrbqgbzkba2.png" alt="Screen Shot 2020-11-01 at 13.02.07"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Visualizing your dedication to writing code</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Sun, 01 Nov 2020 10:12:42 +0000</pubDate>
      <link>https://dev.to/gempain/visualizing-your-dedication-to-writing-code-5aie</link>
      <guid>https://dev.to/gempain/visualizing-your-dedication-to-writing-code-5aie</guid>
      <description>&lt;p&gt;I'm a huge fan of visualizations and how you can get data to tell stories 📖.&lt;/p&gt;

&lt;p&gt;Today I stumbled upon my activity diagram in Gitlab. It made me realize in a glimpse how little I knew the time I dedicate writing code 🧑‍🚀.&lt;/p&gt;

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

&lt;p&gt;It's always interesting to look back, analyze and visualize data, to get a different angle from something you are involved with.&lt;/p&gt;

&lt;p&gt;Gitlab, Github and Gitea offer similar visualizations, and I think Bitbucket provides slightly different graphs.&lt;/p&gt;

&lt;p&gt;Do you know of any tool that helps understand your commitment to writing code by visualizing data in an authentic and meaningful manner ?&lt;/p&gt;

&lt;p&gt;Share yours !&lt;/p&gt;

&lt;p&gt;Happy coding 🚀&lt;/p&gt;




&lt;p&gt;I'm also on Twitter &lt;a href="https://twitter.com/gempain"&gt;@gempain&lt;/a&gt; 👍&lt;/p&gt;

</description>
      <category>datascience</category>
      <category>ux</category>
    </item>
    <item>
      <title>How to CI your project like a champ in 2 minutes</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Sun, 20 Sep 2020 14:39:27 +0000</pubDate>
      <link>https://dev.to/gempain/how-to-ci-your-project-like-a-champ-in-2-minutes-ob1</link>
      <guid>https://dev.to/gempain/how-to-ci-your-project-like-a-champ-in-2-minutes-ob1</guid>
      <description>&lt;p&gt;Today I'll be showing you how to setup CI for your project in less than 2 minutes with &lt;a href="https://github/metroline/metroline" rel="noopener noreferrer"&gt;Metroline&lt;/a&gt;, an open source, self hosted CI. I'll be using Gitea for this tutorial but you could use Gitlab or Github.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Metroline (1 minute)
&lt;/h2&gt;

&lt;p&gt;Create a Gitea OAuth app and copy your &lt;strong&gt;Client ID&lt;/strong&gt; and &lt;strong&gt;Client Secret&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcl5sd5f3fl0qo3xs34qw.gif" 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%2Fcl5sd5f3fl0qo3xs34qw.gif" alt="create-gitea-app.mp4"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Copy &lt;a href="https://gist.github.com/gempain/16df50e686c9aeffd8bccfbb1ae697b9" rel="noopener noreferrer"&gt;this&lt;/a&gt; &lt;code&gt;docker-compose.yml&lt;/code&gt; and:&lt;br&gt;
    - set &lt;code&gt;METROLINE_GITEA_CLIENT_ID&lt;/code&gt; to your Gitea client ID&lt;br&gt;
    - set &lt;code&gt;METROLINE_GITEA_CLIENT_SECRET&lt;/code&gt; to your Gitea client secret&lt;br&gt;
    - set &lt;code&gt;METROLINE_GITEA_URL&lt;/code&gt; to the URL of your Gitea server&lt;br&gt;
    - change &lt;code&gt;192.168.43.36&lt;/code&gt; with your IP&lt;br&gt;
    - set &lt;code&gt;METROLINE_JWT_SECRET&lt;/code&gt; with a random secret obtained from &lt;code&gt;openssl rand 32 -hex&lt;/code&gt;&lt;br&gt;
    - set &lt;code&gt;METROLINE_RUNNER_SECRET&lt;/code&gt; with a random secret obtained from &lt;code&gt;openssl rand 32 -hex&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now, run &lt;code&gt;docker-compose up -d&lt;/code&gt; and browse to &lt;code&gt;http://&amp;lt;your-ip&amp;gt;:3000&lt;/code&gt;. Click &lt;strong&gt;Sign in&lt;/strong&gt;. You'll see the list of your repos. Click &lt;strong&gt;Setup&lt;/strong&gt; on your favorite project, and you're all set !&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%2F4r1jjuoiltx5l5tucv31.gif" 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%2F4r1jjuoiltx5l5tucv31.gif" alt="setup-repo.mp4"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Add CI config file and run first pipeline (30 seconds)
&lt;/h2&gt;

&lt;p&gt;At the root of your project, add a &lt;code&gt;.metroline.yml&lt;/code&gt; file with this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node:12-alpine&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
    &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit, push, and you Metroline starts building your commit immediately.&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%2Fiksvcipkjxtqghuferj9.gif" 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%2Fiksvcipkjxtqghuferj9.gif" alt="pipeline.mp4"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Pipeline statuses are reported to Gitea as they change. You'll be able to see it next to the commit, and clicking the commit status will open the pipeline in Metroline.&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%2F7upspext1057ucwqfh4c.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%2F7upspext1057ucwqfh4c.png" alt="commit-status"&gt;&lt;/a&gt; &lt;/p&gt;

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

&lt;p&gt;I've demonstrated the very basics here, but you can configure parallel jobs, secrets, conditionally execute jobs based on the branch or status of upstream jobs, you can also define pipeline environment, and &lt;a href="https://github.com/metroline/metroline#features" rel="noopener noreferrer"&gt;more&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also remember that this will work with Github and Gitlab, so I recommend you checkout the &lt;a href="https://docs.metroline.io/core/installation" rel="noopener noreferrer"&gt;installation instructions&lt;/a&gt; for your favorite Git server.&lt;/p&gt;

&lt;p&gt;Hope you find this useful !&lt;/p&gt;

&lt;p&gt;Follow us on Twitter &lt;a href="//twitter.com/metrolineio"&gt;@metrolineio&lt;/a&gt; to stay tuned, and share &lt;a href="https://github.com/metroline/metroline" rel="noopener noreferrer"&gt;Metroline&lt;/a&gt; around you so we can grow our community !&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>metroline</category>
    </item>
    <item>
      <title>Open Source CICD built with Docker, Node, React, MongoDB and TypeScript</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Thu, 17 Sep 2020 12:26:35 +0000</pubDate>
      <link>https://dev.to/gempain/open-source-cicd-built-with-docker-node-react-mondodb-and-typescript-391c</link>
      <guid>https://dev.to/gempain/open-source-cicd-built-with-docker-node-react-mondodb-and-typescript-391c</guid>
      <description>&lt;h2&gt;
  
  
  😀 TL;DR
&lt;/h2&gt;

&lt;p&gt;Our company is open sourcing Metroline, an in-house CI solution built with Docker, Node, React, MongoDB, Socket.io, D3 and TypeScript. It's self-hosted and works with Gitlab, Gitea and Github.&lt;/p&gt;

&lt;p&gt;Project 👉 &lt;a href="https://github.com/metroline/metroline" rel="noopener noreferrer"&gt;https://github.com/metroline/metroline&lt;/a&gt;&lt;br&gt;
Demo 👉 &lt;a href="https://demo.metroline.io" rel="noopener noreferrer"&gt;https://demo.metroline.io&lt;/a&gt;&lt;br&gt;
Installation 👉 &lt;a href="https://docs.metroline.io/core/installation" rel="noopener noreferrer"&gt;https://docs.metroline.io/core/installation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feuaaq66jpg3tcykmj6l3.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%2Feuaaq66jpg3tcykmj6l3.png" alt="Metroline CI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🤔 TS;WM
&lt;/h2&gt;

&lt;p&gt;We're a small team of 2 devs. We have been using Gitlab and Gitlab CI for over 3 years now. We have since looked for alternatives several times with no luck, until a few weeks ago. We initially tried Drone but it &lt;a href="https://github.com/drone/drone/issues/2009" rel="noopener noreferrer"&gt;doesn't support&lt;/a&gt; Gitlab sub-groups which is blocking for us. Gitlab CI is a great tool, but we have had issues with the cache system where we sometimes had to artifact &lt;code&gt;node_modules&lt;/code&gt; to get builds to work properly. In addition, it can be slow due to copying artifacts and preparing job workspaces.&lt;/p&gt;

&lt;p&gt;A few months ago I started playing with Docker, Node and React to build a small CI proof of concept. The idea of making a CI myself was quite exciting and I felt driven by this feeling you get when everything comes to life. Surely was I even more excited when my proof of concept started actually working.&lt;/p&gt;

&lt;p&gt;I wanted to write a CI that would run jobs inside Docker containers. This has become the standard for all CI platforms and has revolutionized how we build and ship code today. Big ups to Docker for changing our life for the better 👍.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 Choosing technologies
&lt;/h2&gt;

&lt;p&gt;A lot of popular Open Source projects use Go, Ruby, sometimes Node, PHP, etc. I'm a Node and JS fan all the way (I've used Java for 5 years but I'm just way too productive with JS), so the question wasn't really difficult. Plus, I wanted to practice React and keep the backend a pure ExpressJS app. MongoDB is my favorite DB and the integration overhead in Node is almost non-existant using their &lt;a href="https://www.npmjs.com/package/mongodb" rel="noopener noreferrer"&gt;official driver&lt;/a&gt;. I actually haven't used an ORM at all, and it's just so much easier when the number of layers between your API call and the actual DB is low - but that's another story.&lt;/p&gt;

&lt;p&gt;I wanted things to be live, so I went with &lt;a href="https://socket.io/" rel="noopener noreferrer"&gt;Socket.io&lt;/a&gt;, their API is well designed and it's not too hard to get it right the first time.&lt;/p&gt;

&lt;p&gt;CIs out there have a lot of ways to display jobs/pipelines. I wanted to be able to model a workflow without constraints (such as using stages in Gitlab CI). To model this, I've played around with D3. I've obtained some pretty good results, but I would love if one of you D3 masters out there can help enhance the graph we have.&lt;/p&gt;

&lt;h2&gt;
  
  
  💻 Work, work, work
&lt;/h2&gt;

&lt;p&gt;After a few evenings and weekends writing the bare bones, I had something working. I could trigger jobs and see live logs.&lt;/p&gt;

&lt;p&gt;I showed this to my business partner and we decided that I would work on this full time to get something we could use to replace Gitlab CI inside our company. It's a good learning experience and there are great technical challenges.&lt;/p&gt;

&lt;p&gt;So I got to the task. Cleaned up the backend and the UI, added environment management, logging, security, Docker images, parallel jobs, secrets, branch protections, and &lt;a href="https://github.com/metroline/metroline#features" rel="noopener noreferrer"&gt;more&lt;/a&gt;. I quickly split the backend into two: server + runner. I haven't worked on multi-machine setup yet, because cross-machine workspace management is still being analyzed, but you can already have multiple runners on the same machine.&lt;/p&gt;

&lt;p&gt;I initially used Gitea because it was easy to develop with, but I quickly added Gitlab and Github support and structured the code so that it would be easy to extend to other Git servers (PRs welcome !).&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 What's next
&lt;/h2&gt;

&lt;p&gt;The UI, server and runner as well as docs are all published on Github:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/metroline/metroline" rel="noopener noreferrer"&gt;Server + Runner&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/metroline/metroline-ui" rel="noopener noreferrer"&gt;UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/metroline/metroline-docs" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, the platform has all the features that we use in Gitlab CI, but there are a bunch of things we'd like to add, and plenty of improvements we'd love to make. We've got features planned for pipeline approvals, multi-machine runners, build badges, and a lot more. PRs, issues, questions, all is welcome and will be greatly appreciated.&lt;/p&gt;

&lt;p&gt;And, if you ever happen to feel bored, have spare time, have already watched all the pictures of kittens available on the World Wide Web, and if you're so desperate to find something to do, then join us in the extraordinary boredom of writing Jest tests.&lt;/p&gt;

&lt;p&gt;In the name of our humble team of two, I sincerely hope you enjoyed this post and that we'll be hearing from you. Let us know on twitter &lt;a href="https://twitter.com/metrolineio" rel="noopener noreferrer"&gt;@metrolineio&lt;/a&gt; !&lt;/p&gt;

&lt;p&gt;Cheers 🍻&lt;/p&gt;

</description>
      <category>docker</category>
      <category>node</category>
      <category>react</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Are your Ruby gems up to date ?</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Mon, 17 Aug 2020 16:13:35 +0000</pubDate>
      <link>https://dev.to/gempain/are-your-ruby-gems-up-to-date-2edf</link>
      <guid>https://dev.to/gempain/are-your-ruby-gems-up-to-date-2edf</guid>
      <description>&lt;p&gt;Do you write Ruby applications ? Are your gems up to date ? You can now automatically update your Ruby gems with our CLI v1.10.0+ ! &lt;/p&gt;

&lt;p&gt;All you need to do is update your &lt;code&gt;.pmbot.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
&lt;span class="na"&gt;packageManagers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;packageManager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruby&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and use our latest &lt;code&gt;pmbot/bot:ruby&lt;/code&gt; Docker image that embeds Ruby 2.7 and Bundler 2.1.4.&lt;/p&gt;

&lt;p&gt;Go secure !&lt;/p&gt;

&lt;p&gt;Read further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.pmbot.io/automated-dependency-updates-with-ruby-and-pmbot/"&gt;the official blog post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.pmbot.io/package-managers/ruby"&gt;Ruby package manager plugin docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pmbot.io"&gt;our official website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>PHP and Composer lovers, this if for you</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Tue, 04 Aug 2020 17:14:59 +0000</pubDate>
      <link>https://dev.to/gempain/php-and-composer-lovers-this-if-for-you-2ilp</link>
      <guid>https://dev.to/gempain/php-and-composer-lovers-this-if-for-you-2ilp</guid>
      <description>&lt;p&gt;We all know how it goes... our PHP Composer dependencies get out of date and we forget to update them, and that's not good if we want to keep shipping safe apps. I'd recommend using an automated dependency update bot: &lt;a href="https://pmbot.io"&gt;Pmbot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All you need to do is to use our latest CLI v1.9.0 and use our &lt;code&gt;composer&lt;/code&gt; plugin in your &lt;code&gt;.pmbot.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
&lt;span class="na"&gt;packageManagers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;packageManager&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you haven't done it yet, install Pmbot with Docker in a couple of minutes: instructions &lt;a href="https://docs.pmbot.io/core/installation"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ruby/Gem lovers, stay tuned !&lt;/p&gt;




&lt;p&gt;Official website: &lt;a href="https://pmbot.io"&gt;https://pmbot.io&lt;/a&gt;&lt;br&gt;
Blog: &lt;a href="https://blog.pmbot.io"&gt;https://blog.pmbot.io&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://docs.pmbot.io"&gt;https://docs.pmbot.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>security</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>This Angular helper function has saved me from memory leaks for 4 years</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Mon, 03 Aug 2020 17:30:34 +0000</pubDate>
      <link>https://dev.to/gempain/this-angular-helper-function-has-prevented-me-from-memory-leaks-for-4-years-427h</link>
      <guid>https://dev.to/gempain/this-angular-helper-function-has-prevented-me-from-memory-leaks-for-4-years-427h</guid>
      <description>&lt;p&gt;I have been working with Angular for more than 4 years now. From Angular 2 to Angular 10, this helper function has followed me everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Subscription&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;Subscription&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;subscriptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;subscription&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;closed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unsubscribe&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here's how to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;div&amp;gt;
      {{counter}}
    &amp;lt;/div&amp;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;class&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OnDestroy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;subscriptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&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;ngOnInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// memory leak ahead !&lt;/span&gt;
      &lt;span class="nx"&gt;timer&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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="o"&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="nx"&gt;ngOnDestroy&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscriptions&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;Now, when your component is destroyed, you are safe from memory leaks as you have properly unsubscribed from the RxJS observable.&lt;/p&gt;

</description>
      <category>angular</category>
    </item>
    <item>
      <title>Gitea and Drone CI lovers, this is for you</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Mon, 03 Aug 2020 17:05:21 +0000</pubDate>
      <link>https://dev.to/gempain/gitea-and-drone-ci-lovers-this-is-for-you-o8c</link>
      <guid>https://dev.to/gempain/gitea-and-drone-ci-lovers-this-is-for-you-o8c</guid>
      <description>&lt;p&gt;You love Gitea and we do too ! This weekend, we've added Gitea support to &lt;a href="https://pmbot.io"&gt;Pmbot&lt;/a&gt;. You will now be able to do automated dependency updates of your Gitea projects using DroneCI.&lt;/p&gt;

&lt;p&gt;We've written a blog post for you to get started ! Check it out at &lt;a href="https://blog.pmbot.io/announcing-support-for-gitea/"&gt;https://blog.pmbot.io/announcing-support-for-gitea/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our your deps up to date ?&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>devops</category>
      <category>gitea</category>
      <category>droneci</category>
    </item>
    <item>
      <title>The magic behind IDE autocompletion for files like angular.json</title>
      <dc:creator>Geoffroy Empain</dc:creator>
      <pubDate>Mon, 03 Aug 2020 12:49:27 +0000</pubDate>
      <link>https://dev.to/gempain/the-magic-behind-ide-autocompletion-for-files-like-angular-json-3a2e</link>
      <guid>https://dev.to/gempain/the-magic-behind-ide-autocompletion-for-files-like-angular-json-3a2e</guid>
      <description>&lt;p&gt;We all use frameworks like Angular, React and tools like ESLint, Npm, etc.&lt;/p&gt;

&lt;p&gt;Have you ever wondered how your IDE gives you autocompletion for files like &lt;code&gt;angular.json&lt;/code&gt;, &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;.eslintrc&lt;/code&gt;, and many more ?&lt;/p&gt;

&lt;p&gt;Well, they match your files against several remote JSON Schema stores, among which the most popular is &lt;a href="https://www.schemastore.org/json/"&gt;schemastore.org&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Their &lt;a href="https://www.schemastore.org/api/json/catalog.json"&gt;API&lt;/a&gt; provides a catalog of JSON Schemas that describe various commonly used files.&lt;/p&gt;

&lt;p&gt;There are two types of entries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;static&lt;/strong&gt;: references a file stored directly on &lt;a href="https://www.schemastore.org/json/"&gt;schemastore.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dynamic&lt;/strong&gt;: references a URL which points to a specific JSON Schema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The advantage of &lt;strong&gt;dynamic&lt;/strong&gt; entries is that you can update your JSON Schema without having to update your entry in the catalog, and that's exactly what &lt;code&gt;.angular-cli.json&lt;/code&gt; does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.angular-cli.json"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Angular&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CLI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;configuration&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;file"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fileMatch"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.angular-cli.json"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;angular-cli.json"&lt;/span&gt;&lt;span class="pi"&gt;],&lt;/span&gt;
  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url"&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://raw.githubusercontent.com/angular/angular-cli/master/packages/angular/cli/lib/config/schema.json"&lt;/span&gt;
&lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At &lt;a href="//https:/pmbot.io"&gt;Pmbot&lt;/a&gt;, we automatically generate and publish the JSON Schema of our CLI configuration file to our &lt;a href="https://github.com/pmbot-io/config"&gt;Github config repo&lt;/a&gt; every time we release a new version of our CLI. The entry for our &lt;a href="https://docs.pmbot.io/pmbot-yml/pmbot-yml"&gt;&lt;code&gt;.pmbot.yml&lt;/code&gt;&lt;/a&gt; in the &lt;a href="https://www.schemastore.org/json/"&gt;schemastore.org&lt;/a&gt;  points to the raw JSON Schema file hosted on Github.&lt;/p&gt;

&lt;p&gt;Thanks for reading !&lt;/p&gt;

</description>
      <category>devops</category>
      <category>vscode</category>
    </item>
  </channel>
</rss>
