<?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: Bruno Paz</title>
    <description>The latest articles on DEV Community by Bruno Paz (@brpaz).</description>
    <link>https://dev.to/brpaz</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%2F47185%2F5ba8dbf6-b165-4360-93e8-c56a8a7c0a9f.jpeg</url>
      <title>DEV Community: Bruno Paz</title>
      <link>https://dev.to/brpaz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/brpaz"/>
    <language>en</language>
    <item>
      <title>How to keep software installed from GitHub updated, with Ansible.</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Fri, 11 Jun 2021 14:22:55 +0000</pubDate>
      <link>https://dev.to/brpaz/how-to-keep-software-installed-from-github-updated-with-ansible-4lbe</link>
      <guid>https://dev.to/brpaz/how-to-keep-software-installed-from-github-updated-with-ansible-4lbe</guid>
      <description>&lt;p&gt;The simplest way to install software on Linux based Operating Systems is to use the default package manager that comes with the OS, like &lt;code&gt;apt&lt;/code&gt; in Debian/Ubuntu or &lt;code&gt;dnf&lt;/code&gt; in Fedora. &lt;/p&gt;

&lt;p&gt;Submitting software and maintaining these repositories is not trivial for the common developer, and so there are lot´s of great software that is simply not available or is outdated on the package managers repositories.&lt;/p&gt;

&lt;p&gt;Flatpaks and Snaps are growing as a viable alternative to distribute software, but still, there are tons of great Software that the only way to get it, is from GitHub.&lt;/p&gt;

&lt;p&gt;If the repository has a good release process, installing software from GitHub can be almost as easy as using a package manager. &lt;/p&gt;

&lt;p&gt;Many projects provide artifacts in multiple formats like &lt;code&gt;deb&lt;/code&gt;, &lt;code&gt;rpm&lt;/code&gt;, &lt;code&gt;AppImage&lt;/code&gt; and you can choose the most convenient way for your system. &lt;/p&gt;

&lt;p&gt;If that is not available, at least you should have a tarball that you can download and extract it´s contents to the appropriate place on your System.&lt;/p&gt;

&lt;p&gt;The biggest issue with installing software directly from GitHub is the update process. &lt;/p&gt;

&lt;p&gt;When you use any kind of package manager, updating all software is a matter of running a single command like &lt;code&gt;apt update&lt;/code&gt; or equivalent. &lt;/p&gt;

&lt;p&gt;For software installed from GitHub, the process is a lot more manual as you have to go to the GitHub repository, check if there is a new release, download and install the correct artifact and repeat the process for each software that you have installed this way.&lt;/p&gt;

&lt;p&gt;What if we could automate this process? This is where &lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; comes in. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is Ansible?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.ansible.com/"&gt;Ansible&lt;/a&gt; is a tool for automating cloud provisioning, configuration management and application deployment. It´s mostly used by SysAdmins/DevOps to manage multiple servers as infrastructure as code, but the abstractions are the same whether you're managing one machine or a hundred.&lt;/p&gt;

&lt;p&gt;Under the hood, Ansible is just a domain specific language (DSL) for a task runner that runs over ssh. You write Ansible yaml files which describe the tasks that you want to run on each machine and Ansible will take care of running these tasks and ensure that the state of your system matches, matches what is specified.&lt;/p&gt;

&lt;p&gt;Ansible includes many built-in modules, like "copy" or "command", that abstracts common actions like copy files or execute shell commands. You can check all the available built-in modules &lt;a href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/index.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are also many 3rd party modules, built by the community, that you can use. &lt;/p&gt;

&lt;p&gt;In this article we will use the &lt;a href="https://docs.ansible.com/ansible/latest/collections/community/general/github_release_module.html"&gt;GitHub release module&lt;/a&gt;, which provides an interface to the GitHub Releases API, and will allow us to get the latest release of a particular repository, in our Ansible tasks.&lt;/p&gt;

&lt;p&gt;Ansible is very powerful, and we will only surface the basics on this article. I think it´s important to explain some terminology before going further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Playbook&lt;/strong&gt;: A playbook consists of a set of tasks that you want to run on the target machine. It´s the entry-point for the Ansible execution. These tasks can be defined directly in the playbook file or included from other Ansible files or roles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role&lt;/strong&gt;: A role is the primary mechanism for breaking a playbook into multiple files. This simplifies writing complex playbooks, and it makes them easier to reuse. For Example you can have a "docker" role that installs and configures docker, and use that role in your playbook, instead of repeating the same tasks in all your playbooks that needs docker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module&lt;/strong&gt; - A module is a reusable, standalone script that Ansible runs on your behalf, either locally or remotely. Modules interact with your local machine, an API, or a remote system to perform specific tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inventory&lt;/strong&gt; - A list or a group of machines/hosts where Ansible will be run against.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install GitHub Cli using Ansible
&lt;/h2&gt;

&lt;p&gt;To demonstrate the use of Ansbile to install software from GitHub, we will create simple playbook to install the &lt;a href="https://github.com/cli/cli/"&gt;GitHub CLI&lt;/a&gt; tool, on an Ubuntu based system. You can find the full source code at &lt;a href="https://github.com/brpaz/install-github-software-ansible-demo"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Ansible and dependencies
&lt;/h3&gt;

&lt;p&gt;Ansible is available in the default repositories for most popular distros. You can also install it using Python Pip.&lt;/p&gt;

&lt;p&gt;For ubuntu 20.04, we could install it with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python3-pip ansible
ansible &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check the specific instructions for your distro on the &lt;a href="https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html"&gt;Installation Guide&lt;/a&gt; in Ansible Docs.&lt;/p&gt;

&lt;p&gt;We also need to install the &lt;a href="https://docs.ansible.com/ansible/2.5/modules/github_release_module.html"&gt;github_release&lt;/a&gt; Ansible module. This module is part of the "community.general" collection, that you can install using &lt;a href="https://galaxy.ansible.com"&gt;Ansible Galaxy&lt;/a&gt;, a package manager for Ansible content. A Collection is a distribution format for Ansible content that can include playbooks, roles, modules, and plugins. &lt;/p&gt;

&lt;p&gt;Ansible Galaxy is included when you install Ansible. You can run the following command to install the respective collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-galaxy collection &lt;span class="nb"&gt;install &lt;/span&gt;community.general
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Ansible module depends on the &lt;code&gt;github.py&lt;/code&gt; Python package to interact with the GitHub API. It´s a Python package, so we can install it with Python Pip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;github3.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating the playbook.
&lt;/h3&gt;

&lt;p&gt;Creating an Ansible playbook is as simple as creating an YAML file following the structure required by Ansible.&lt;/p&gt;

&lt;p&gt;The full playbook for this example, will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;GitHub Cli install&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;

  &lt;span class="na"&gt;vars_prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github_token&lt;/span&gt;
      &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Token?"&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lookup('env','GITHUB_TOKEN')&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;

  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Latest&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Github"&lt;/span&gt;
      &lt;span class="s"&gt;community.general.github_release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cli&lt;/span&gt;
        &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cli&lt;/span&gt;
        &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest_release&lt;/span&gt;
        &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github_token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&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;Print Latest release&lt;/span&gt;
      &lt;span class="s"&gt;ansible.builtin.debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;var&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&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;Download Binary&lt;/span&gt;
      &lt;span class="s"&gt;ansible.builtin.unarchive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/cli/cli/releases/download/{{release.tag}}/gh_{{release.tag[1:]}}_linux_amd64.tar.gz&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp&lt;/span&gt;
        &lt;span class="na"&gt;remote_src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Binary&lt;/span&gt;
      &lt;span class="s"&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/gh_{{release.tag[1:]}}_linux_amd64/bin/gh&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin"&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;a+x&lt;/span&gt;
      &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;hosts&lt;/strong&gt; property is used to specify in which machine(s) this playbook will be run. This is mostly useful, for multiple servers orchestration, as you might want to run different tasks on different hosts, depending on the role of the server for example (web, database etc). &lt;/p&gt;

&lt;p&gt;Since this will be run only on a single machine, we can use the keyword "all".&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;var_prompts&lt;/code&gt; section, allow us to specify a list of variables that Ansible will prompt the user before running the playbook. In this case we will ask for a GitHub token, defaulting to the value of "GITHUB_TOKEN" environment variable.&lt;/p&gt;

&lt;p&gt;This isn´t really needed for this simple example, but if you are doing many requests to GitHub API in the same Ansible run, it might be wise to set it, to avoid rate limits of the GitHub API.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;tasks&lt;/strong&gt; section is where we specify the commands that we want Ansible to run on each host. These commands will be defined with Ansible modules. &lt;/p&gt;

&lt;p&gt;Each task will be executed in the defined order.&lt;/p&gt;

&lt;p&gt;So, If we wanted to manually install the GitHub Cli from GitHub, these would be the steps that we would do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go the the &lt;a href="https://github.com/cli/cli/releases"&gt;releases page&lt;/a&gt; to see what is the latest available release.&lt;/li&gt;
&lt;li&gt;Download the appropriate artifact for our system.&lt;/li&gt;
&lt;li&gt;Depending on the format of the artifact, we could install it directly, or if a tarball, for example, we would need to extract it and move the contents to the appropriate place.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let´s see how we can automate these steps with Ansible.&lt;/p&gt;

&lt;p&gt;To get the latest release, we will use the "github_release" module we installed before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Get&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Latest&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Github"&lt;/span&gt;
    &lt;span class="s"&gt;community.general.github_release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cli&lt;/span&gt;
      &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cli&lt;/span&gt;
      &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest_release&lt;/span&gt;
      &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;github_token&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;register&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We specify some options like the "user" and "repo" and also the "register" property is used, so we store the output of the command in a variable to be used in the next steps.&lt;/p&gt;

&lt;p&gt;We can see the value of the variable for debug purposes, using the "debug" module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print Latest release&lt;/span&gt;
  &lt;span class="s"&gt;ansible.builtin.debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;var&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to download the respective artifact from GitHub. This step can be a little different depending on the project and how they create the releases.&lt;/p&gt;

&lt;p&gt;As we can see in the &lt;a href="https://github.com/cli/cli/releases"&gt;releases page&lt;/a&gt;, this project offers artifacts for deb, rpm, and archive (tar.gz).  For this example, we will use the archive version as it is the most common used format that we will probably find. But if the project have a better format available for your system, you should use that. If you are using Ubuntu, you could use the &lt;a href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/apt_module.html"&gt;APT module&lt;/a&gt; to install the .deb file directly instead.&lt;/p&gt;

&lt;p&gt;For downloading and extracting the tarball, we will use the built-in &lt;a href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/unarchive_module.html"&gt;Unarchive&lt;/a&gt; module, that can do both at the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Download Binary&lt;/span&gt;
   &lt;span class="s"&gt;ansible.builtin.unarchive&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/cli/cli/releases/download/{{release.tag}}/gh_{{release.tag[1:]}}_linux_amd64.tar.gz&lt;/span&gt;
    &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp&lt;/span&gt;
    &lt;span class="na"&gt;remote_src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we use the &lt;code&gt;release&lt;/code&gt; variable that we saved in the previous step, to construct the full download link for the archive. The unarchive module will automatically download and extract the file specified in the "src" property to the directory specified in the "dest" property. The "remote_src" flag is needed to indicate Ansible that the source is a remote URL.&lt;/p&gt;

&lt;p&gt;After we download and extract the artifact, we can move the respective executable file to a place in your PATH, like &lt;code&gt;/usr/local/bin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can use another built-in Ansible module, "copy" do to that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Binary&lt;/span&gt;
      &lt;span class="s"&gt;ansible.builtin.copy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/gh_{{release.tag[1:]}}_linux_amd64/bin/gh&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin"&lt;/span&gt;
        &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;a+x&lt;/span&gt;
      &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file needs to be executable, so we specify the "mode" property to indicate the respective permissions. The "become" property is used to run the command as sudo, since the "/usr/local/bin" is usually owned by the root user.&lt;/p&gt;

&lt;p&gt;And that´s it.&lt;/p&gt;

&lt;p&gt;To run the playbook, open a terminal in the directory where your playbook and hosts file is located, and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ansible-playbook &lt;span class="nt"&gt;-i&lt;/span&gt; hosts setup.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-i&lt;/code&gt; flag indicates a path to the "inventory" file, which specifies the ip addresses and other connection properties that will be used by Ansible to connect to the target machine. Ansible works by connecting to the target machine via SSH, but since we are running this playbook locally, we can use the property "ansible_connection" to indicate that in the hosts file.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;After Ansible is executed , when we open a new terminal and type &lt;code&gt;gh&lt;/code&gt;, it should show the GitHub CLI help command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using it at scale.
&lt;/h2&gt;

&lt;p&gt;This example, showed the basics of using Ansible to install GitHub software. The tasks will vary slightly depending on the project and what kind of artifacts they provide. &lt;/p&gt;

&lt;p&gt;While the example installs a single piece of software, you could do exactly the same logic to install many software from multiple GitHub repositories in the same Ansible playbook.&lt;/p&gt;

&lt;p&gt;You could define all your tasks in the playbook file, but like in programming, when your playbook gets too big, it´s recommended to extract into seperate files. Think of the playbook file as the "main" function of your program. You can do this by using &lt;a href="https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html"&gt;roles&lt;/a&gt; and create a single role for each software, or you can just create seperate yaml files, each one containing the tasks for each software and then using the &lt;a href="https://docs.ansible.com/ansible/latest/collections/ansible/builtin/include_tasks_module.html"&gt;Include tasks&lt;/a&gt; directive, to include the tasks in the main playbook file.&lt;/p&gt;

&lt;p&gt;Roles are most useful if you want to reuse functionality across different playbooks or share with the community and also if your tasks are more complex or requires same more configuration.&lt;/p&gt;

&lt;p&gt;In our example, we could encapsulate all the tasks defined in the main playbook into a role and then reference it from the playbook file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;
  &lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;github-cli&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://github.com/tommarshall/ansible-role-awscli"&gt;this&lt;/a&gt; example of a role that installs the AWS Cli.&lt;/p&gt;

&lt;p&gt;I am using "include_tasks" in my setup and use normal folders to organize each software as most of the tasks are very simple, but if I was starting today, I would probably use roles, which is a more "standard" way and could allow me to share them to other persons.&lt;/p&gt;

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

&lt;p&gt;GitHub is a great source of software, but keeping all of it updated with the latest release, can be a very manual process and a lot less convenient that using your distribution package manager. Ansible can help automating that bit.&lt;/p&gt;

&lt;p&gt;Create a playbook and tasks for each of your software, and then every time you want to update your system, simply execute the Playbook, in a similar way as you would run &lt;code&gt;apt update&lt;/code&gt; or &lt;code&gt;flatpak update&lt;/code&gt;. The "github_release" module will take care, of getting the latest release directly from GitHub, so every time you run the playbook, you will install the most recent version of the Software.&lt;/p&gt;

&lt;p&gt;Ansible can be used for a lot more than just installing GitHub software. You can completely automate the setup of a new machine with it. Check my personal setup &lt;a href="https://github.com/brpaz/my-linux-setup/"&gt;here&lt;/a&gt; for inspiration.&lt;/p&gt;

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

</description>
      <category>github</category>
      <category>ansible</category>
    </item>
    <item>
      <title>Serverless beyond FaaS</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Tue, 07 Jul 2020 22:01:30 +0000</pubDate>
      <link>https://dev.to/brpaz/serverless-beyond-faas-5fo</link>
      <guid>https://dev.to/brpaz/serverless-beyond-faas-5fo</guid>
      <description>&lt;p&gt;Microservices are dead!! Serverless is for some time, the new hype in the infrastructure world. &lt;/p&gt;

&lt;p&gt;One of the main advantages of a Serverless Architecture is that it frees developers from having to worry about server maintenance, scalability, etc, as these will be handled automatically by the Serverless platform provider, so they can focus entirely on building great products.&lt;/p&gt;

&lt;p&gt;This is great, mostly for single devs and small teams as server management, even at a small scale, can become a complex and time-consuming task.&lt;/p&gt;

&lt;p&gt;One particular type of Serverless, and perhaps the most popular one, to the point it´s often mixed up with the concept itself, is Functions as Service (FaaS), also known as lambdas, due to &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt;, which is one of the most popular providers of this technology.&lt;/p&gt;

&lt;p&gt;The main idea behind lambdas is that you build small independent functions, each one responsible for a small part of your application. &lt;/p&gt;

&lt;p&gt;Considering a traditional REST API, you can imagine that any Controller Action would be a separate function.&lt;/p&gt;

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

&lt;p&gt;So now, instead of having 50 Microservices, you have 1000 functions! ;)&lt;/p&gt;

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

&lt;p&gt;It´s true that with a FaaS platform, you won´t need to worry about managing the servers, scalability, etc that would take a lot of effort to do it right, in a big Microservices Architecture. &lt;/p&gt;

&lt;p&gt;But you still have to deal with all the coordination between  the functions. &lt;br&gt;
You will also have extra complexity for devs to develop and test locally.&lt;/p&gt;

&lt;p&gt;And because each provider implements its FaaS platform differently, the lock-in is very high. &lt;/p&gt;

&lt;p&gt;Besides, the application will have to be designed in a specific way, tied, to a specific deployment style, so you really can´t move back from a full FaaS architecture to another kind of architecture that easily.&lt;/p&gt;

&lt;p&gt;The idea of this article is not against Lambdas. There are lots of great use cases for them, like Event-driven applications, very specific tasks like Image Processing, Sending emails, and even as a small backend for static sites.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I would like you to remember from this article, is that Lambda´s != Serverless and there is Serverless beyond Lambdas!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have seen devs, trying to squeeze existing applications into Lambdas, like it´s the only way to deploy applications.&lt;/p&gt;

&lt;p&gt;Or people creating issues on Open Source projects like &lt;a href="https://strapi.io/"&gt;Strapi&lt;/a&gt;, asking to make it run on Lambda. Or discussing ways to run Laravel on Lambda, or Rails on Lambda.&lt;/p&gt;

&lt;p&gt;I understand the appeal. no server maintenance, almost zero cost for small/medium size projects. But we don´t need to be constrained by FaaS limitations to enjoy the benefits of Serverless. There are alternatives.&lt;/p&gt;

&lt;p&gt;One great example is &lt;a href="https://cloud.google.com/run"&gt;Google Cloud Run&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Google cloud Run fits perfectly my vision of Serverless. &lt;/p&gt;

&lt;p&gt;You just need to have a Docker image and you can deploy it with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run deploy &lt;span class="nt"&gt;--image&lt;/span&gt; &lt;span class="s2"&gt;"myimage"&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; managed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No need to change the way you build applications. No limitations on technology. If it can be containerized you can deploy it on Cloud Run.&lt;/p&gt;

&lt;p&gt;With Cloud Run, you get all the main selling points of serverless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to worry about server maintenance.&lt;/li&gt;
&lt;li&gt;Automatic Autoscaling (including scaling to zero, when the application is not in use)&lt;/li&gt;
&lt;li&gt;Pay only for what you use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without the limitations of Lambdas.&lt;/p&gt;

&lt;p&gt;I think we need more platforms like Cloud Run, with I feel like an evolution of more traditional PaaS like Heroku. &lt;/p&gt;

&lt;p&gt;Cloud Run, is build on top of &lt;a href="https://knative.dev/"&gt;Knative&lt;/a&gt;. which is an open-source Kubernetes-based platform to deploy and manage modern serverless workloads, so others could use the same foundations.&lt;/p&gt;

&lt;p&gt;I know AWS has &lt;a href="https://aws.amazon.com/fargate/"&gt;Fargate&lt;/a&gt;, which seems to require some more configuration but should be the closest equivalent of Cloud Run in AWS.&lt;/p&gt;

&lt;p&gt;I am also looking forward to what comes out from the &lt;a href="https://www.digitalocean.com/nanobox/"&gt;DigitalOcean/Nanobox&lt;/a&gt; integration.&lt;/p&gt;

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

&lt;p&gt;Serverless is a great concept and I believe will keep maturing in the next years. &lt;/p&gt;

&lt;p&gt;It won´t replace traditional servers for more complex applications or when more control is needed.&lt;/p&gt;

&lt;p&gt;But, for smaller teams and single developers, who want to build a product without having concerns about scalability and server maintenance and with the extra benefit of only Pay for what is used, is perfect.&lt;/p&gt;

&lt;p&gt;It´s important to understand that Serverless is not just FaaS. Platforms like Google Cloud Run are a great example and might be a much simpler alternative for deploying your applications instead of trying to squeeze them into a Lambda.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>devops</category>
    </item>
    <item>
      <title>Paid Developer tools you can´t live without?</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Fri, 03 Jul 2020 15:09:11 +0000</pubDate>
      <link>https://dev.to/brpaz/paid-developer-tools-you-can-t-live-without-1epm</link>
      <guid>https://dev.to/brpaz/paid-developer-tools-you-can-t-live-without-1epm</guid>
      <description>&lt;p&gt;In general, I believe most Developers always tend to look for using Free or Open Source Tools. &lt;/p&gt;

&lt;p&gt;And the fact is, thanks to the amazing community of Open Source Developers, you can have all the essential development tools for free!&lt;/p&gt;

&lt;p&gt;A personal fact, and maybe it´s something that comes with the age or experience, and by getting a better understanding of how valuable development time is, I am realizing that I am more open than ever to pay a fair price for top quality software, sponsoring other Devs, etc.&lt;/p&gt;

&lt;p&gt;So I decided to write this article, to open the discussion: What paid software do you use on a daily basis that you can´t live without?&lt;/p&gt;

&lt;p&gt;It doesn't need to be a Software per se, can be some site template you bought, for example. Some resource or tool that you gladly paid for it, that makes your life as Dev much easier.&lt;/p&gt;

&lt;p&gt;I can start naming a few:&lt;/p&gt;

&lt;h2&gt;
  
  
  Jetbrains IDEs
&lt;/h2&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%2F7gkon7ev72sgd61jei33.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%2F7gkon7ev72sgd61jei33.png" alt="Jetbrains"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While there are awesome Open Source Code Editors like &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt;, &lt;a href="https://www.jetbrains.com/" rel="noopener noreferrer"&gt;JetBrains&lt;/a&gt; IDEs are very popular and powerful. &lt;/p&gt;

&lt;p&gt;Some of the features I highlight are the powerful refactoring tools and much more reliable and faster code completion.&lt;/p&gt;

&lt;p&gt;I use a mix of VS Code and Jetbrains IDEs (Goland, PHPStorm, WebStorm) but for more "serious" and complex projects, I tend to prefer Jetbrains IDEs.&lt;/p&gt;

&lt;p&gt;VSCode is closing the gap and has many more contributions in terms of Extensions and I would love to use it fully, but there are some tasks that Jetbrains is still far superior.&lt;/p&gt;

&lt;p&gt;It´s definitely a very powerful software that you might be willing to pay for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cacher
&lt;/h2&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%2Fk6gpdg7821wee1n0fq80.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%2Fk6gpdg7821wee1n0fq80.png" alt="Cacher Logo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cacher.io/" rel="noopener noreferrer"&gt;Cacher&lt;/a&gt; is a Code Snippets management tool that helps to organize your snippets library with labels and a powerful search engine and everything is Synced to GitHub Gist. &lt;/p&gt;

&lt;p&gt;It´s cross-platform and also provides deep integration with the most popular Editors like VS Code, Jetbrains, Atom, and Sublime.&lt;/p&gt;

&lt;p&gt;I couldn´t find anything free with this level of quality and features. &lt;/p&gt;

&lt;p&gt;With the usage I am giving to, the personal plan of 6$/month is completely worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tailwind UI
&lt;/h2&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%2F3t81uhuf0mw77inmpfzc.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%2F3t81uhuf0mw77inmpfzc.png" alt="TailwindUI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tailwindui.com" rel="noopener noreferrer"&gt;TailwindUI&lt;/a&gt; is not an application but I think it deserves a mention here.&lt;/p&gt;

&lt;p&gt;It is a blessing for backend developers like me who suck at design.&lt;/p&gt;

&lt;p&gt;It provides a high-quality set of all kinds of components for building any kind of Web UI, from an application to marketing and landing pages. &lt;br&gt;
The components are pure HTML with Tailwind CSS.&lt;/p&gt;

&lt;p&gt;You can see what is included &lt;a href="https://tailwindui.com/components" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The price range from $149 to 249$ one-time payment.&lt;/p&gt;

&lt;p&gt;It can look expensive but believe me, it´s an amazing toolkit for building good looking websites and you can see the amount of work put on it. &lt;/p&gt;

&lt;p&gt;If you want to build a web application but suck at design, it´s a great investment.&lt;/p&gt;




&lt;p&gt;Now, I want to hear from you!&lt;/p&gt;

&lt;p&gt;And also start streaming your micro-payments. Let´s see how this Web Monetization thingy really works. 😛&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>tools</category>
    </item>
    <item>
      <title>How to create your own auto-completion for JSON and YAML files on VS Code with the help of JSON Schema</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Mon, 27 Apr 2020 20:51:52 +0000</pubDate>
      <link>https://dev.to/brpaz/how-to-create-your-own-auto-completion-for-json-and-yaml-files-on-vs-code-with-the-help-of-json-schema-k1i</link>
      <guid>https://dev.to/brpaz/how-to-create-your-own-auto-completion-for-json-and-yaml-files-on-vs-code-with-the-help-of-json-schema-k1i</guid>
      <description>&lt;p&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; has the ability to display autocomplete suggestions for popular configuration files in JSON and YAML format out of the box.&lt;/p&gt;

&lt;p&gt;For example, If you have a &lt;code&gt;package.json&lt;/code&gt; file opened in VS Code, you can tap &lt;code&gt;CTRL + Space&lt;/code&gt; and a pop-up will appear, displaying suggestions for all the available fields for that file type.&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%2F8f9dityz4vsc2uf9hesi.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%2F8f9dityz4vsc2uf9hesi.gif" alt="VS Code autocomplete example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is very practical and provides a quick way of knowing all the available options without having to look at Documentation.&lt;/p&gt;

&lt;p&gt;To be able to do this, VS Code uses &lt;a href="https://json-schema.org/" rel="noopener noreferrer"&gt;JSON Schema&lt;/a&gt; under the hood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; other editors like the ones from the Jetbrains family, also use the same strategy to provide the same functionality, so, while in this article VS Code is used as an example, the concepts should be applicable to any of these IDEs. Be sure to check their respective documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is JSON Schema?
&lt;/h2&gt;

&lt;p&gt;JSON Schema is a specification that allows you to describe the structure of a JSON document and validate documents against that schema.&lt;/p&gt;

&lt;p&gt;For example, the following schema could be used to describe a "Person" entity:&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;"$id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/person.schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://json-schema.org/draft-07/schema#"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Person"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"firstName"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The person's first name."&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;"lastName"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The person's last name."&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;"age"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Age in years which must be equal to or greater than zero."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minimum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;p&gt;As you can see, a JSON Schema is simply a JSON document with a specific set of tags that are used to describe the document structure.&lt;/p&gt;

&lt;p&gt;This is a very basic example. You can check the &lt;a href="https://json-schema.org/specification.html" rel="noopener noreferrer"&gt;JSON Schema&lt;/a&gt; website for a complete view of the specification.&lt;/p&gt;

&lt;p&gt;While JSON Schema is designed to work with JSON files, it can be used to with any other file that can be converted to JSON, like YAML.&lt;/p&gt;

&lt;p&gt;VS Code uses the JSON Schema, to infer the document structure of a particular file type and to display the appropriate suggestions as well as validations. &lt;/p&gt;

&lt;p&gt;To provide the autocomplete capability to a big range of popular file types, VS Code leverages the &lt;a href="http://schemastore.org/json/" rel="noopener noreferrer"&gt;JSON Schema Store&lt;/a&gt; project, which hosts JSON Schema specifications for more than 200 file types.&lt;/p&gt;

&lt;p&gt;The JSON Schema Store provides a very good set of schemas. Still, with so many different file types, of course, it can´t have schemas for everything.&lt;/p&gt;

&lt;p&gt;The good news is that you can easily create your own and make it available for your editor to use.&lt;/p&gt;

&lt;p&gt;And while you are at it, JSON Schema Store is &lt;a href="https://github.com/schemastore/schemastore/" rel="noopener noreferrer"&gt;Open source&lt;/a&gt;, so why not contribute to their repository, so that everyone can benefit from it?&lt;/p&gt;

&lt;p&gt;To demonstrate this, we will create a schema for &lt;a href="https://github.com/hadolint/hadolint" rel="noopener noreferrer"&gt;Hadolint&lt;/a&gt;, a popular Dockerfile linter.&lt;/p&gt;

&lt;p&gt;Hadolint supports configuration with a &lt;code&gt;.hadolint.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;It only allows two configurations to be defined: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ignored - which defines a list of lint rules to ignore &lt;/li&gt;
&lt;li&gt;trustedRegistries - which as the name indicates, it´s a list of valid Docker Registries to allow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example file:&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;ignored&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DL3008&lt;/span&gt;

&lt;span class="na"&gt;trustedRegistries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker.io&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It´s a really simple structure, so a good example to start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating our schema
&lt;/h2&gt;

&lt;p&gt;So we need to create the JSON Schema specification for this file type.&lt;/p&gt;

&lt;p&gt;First, we need to understand the structure, and to find out all the possible options for that particular file type. This task can be easier or not, depending on the available documentation.&lt;/p&gt;

&lt;p&gt;Probably the best way to start is to look at the project documentation or for example files. Some projects provide example files with all the possible values and you can infer the schema from there.&lt;/p&gt;

&lt;p&gt;In the case of Hadolint we can get everything needed from the &lt;a href="https://github.com/hadolint/hadolint#configure" rel="noopener noreferrer"&gt;project Readme&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Next, we will create an &lt;code&gt;hadolint.json&lt;/code&gt; file with the schema specification.&lt;/p&gt;

&lt;p&gt;The shcema will be very similar to this:&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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://json-schema.org/draft-07/schema#"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JSON Schema for Hadolint, a Dockerfile linter tool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile linter, validate inline bash, written in Haskell"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&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;"ignored"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A list of rules to be ignored"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"oneOf"&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;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Use absolute WORKDIR."&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;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"For some bash commands it makes no sense running them in a Docker container like ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig."&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;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Last user should not be root."&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="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Use WORKDIR to switch to a directory."&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;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3004"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Do not use sudo as it leads to unpredictable behavior. Use a tool like gosu to enforce root."&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;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3005"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Do not use apt-get upgrade or dist-upgrade."&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;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rule"&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;span class="nl"&gt;"trustedRegistries"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A list of trusted registries. Ex: docker.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&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;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;p&gt;Note that I have omitted some options on the "ignored" field for readability.&lt;/p&gt;

&lt;p&gt;Let´s break down our schema:&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="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://json-schema.org/draft-07/schema#"&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;This first line describes which version of the schema we are using. We use the "Draft-07", which is the latest version at the time of this article.&lt;/p&gt;

&lt;p&gt;We then define a "title" and a "description" for the schema.&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="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JSON Schema for Hadolint, a Dockerfile linter tool"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile linter, validate inline bash, written in Haskell"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;The root "type" property will have the value "object" most of the time, and we also define "additionalProperties" property with value "false", to indicate that the file can´t have extra properties that are not listed in the schema.&lt;/p&gt;

&lt;p&gt;The "properties" field is where we start defining all the properties of our object.&lt;/p&gt;

&lt;p&gt;As we saw, &lt;code&gt;hadolint.yaml&lt;/code&gt; has two properties: "ignored" and "trustedRegistries" which are both a list of strings.&lt;/p&gt;

&lt;p&gt;For the "trustedRegistries" it´s an open list, while for the "ignored" each option must be one of the rules available in Hadolint.&lt;/p&gt;

&lt;p&gt;"trustedRegistries", can be defined as this:&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="nl"&gt;"trustedRegistries"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A list of trusted registries. Ex: docker.io"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&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;p&gt;We define the "type" as an "array" whose items are of type "string".&lt;/p&gt;

&lt;p&gt;For the "ignored" property, it´s similar but has a fixed list of options.&lt;/p&gt;

&lt;p&gt;It could be defined as this:&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="nl"&gt;"ignored"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A list of rules to be ignored"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"items"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"oneOf"&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;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Use absolute WORKDIR."&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;span class="nl"&gt;"const"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DL3001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"For some bash commands it makes no sense running them in a Docker container like ssh, vim, shutdown, service, ps, free, top, kill, mount, ifconfig."&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;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;p&gt;JSON Schema also has "enum" type. We could have used it instead of type "string" together with "oneOf" field, but as we want to add a description to each value, Enum type doesn't support that.&lt;/p&gt;

&lt;p&gt;With this, we have the basic structure of our schema ready.&lt;/p&gt;

&lt;p&gt;This example just shows the surface of what you can do with JSON Schema. You can see more complex schemas in Schema Store. for example &lt;a href="http://json.schemastore.org/gitlab-ci" rel="noopener noreferrer"&gt;gitlab-ci&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Depending on the complexity of the file whose schema you are creating, it can be a little boring to specify every available option, but if you work with that file a lot, It can save you valuable time in the future.&lt;/p&gt;

&lt;p&gt;Now that we have created the schema, let´s see how we can make it available on VS Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling the Schema on VS Code
&lt;/h2&gt;

&lt;p&gt;Since the new schema is not in the Schema Store, you must register it on VS Code. This can be done on the VS Code Settings page.&lt;/p&gt;

&lt;p&gt;As we are creating a Schema for a YAML file, make sure you have the &lt;a href="https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml" rel="noopener noreferrer"&gt;YAML&lt;/a&gt; Extension installed before continuing.&lt;/p&gt;

&lt;p&gt;Then, go to File -&amp;gt; Preferences -&amp;gt; Settings (or use the Command Pallete) to open the settings page and search for &lt;code&gt;yaml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Open the settings for the YAML extension and search for "Yaml: Schemas" and click "Edit in settings.json".&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%2Fcjyo0kkogh1dnf8s3y1y.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%2Fcjyo0kkogh1dnf8s3y1y.png" alt="VS Code settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The "settings.json" file will open. you need to search again for the "yaml.schemas" object. If it doesn´t exist yet, you will have to create it.&lt;/p&gt;

&lt;p&gt;This property represents a key-value, where the key is the absolute path to the schema file on our system and the value is a glob expression that specifies the files that the schema will be applied. In my case it looks like this:&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="nl"&gt;"yaml.schemas"&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;"/home/bruno/Code/Personal/other/schemastore/src/schemas/json/hadolint.json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".hadolint.yaml"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;


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

&lt;/div&gt;

&lt;p&gt;Save the file and reload VS Code to finish the process.&lt;/p&gt;

&lt;p&gt;If everything worked as expected, when we create a new &lt;code&gt;.hadolint.yaml&lt;/code&gt; file and press &lt;code&gt;CTRL + Space&lt;/code&gt;, VS Code should then display the suggestions based on the schema we created for this file type.&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%2Fjqgnscgmhbf6go2r7r5x.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%2Fjqgnscgmhbf6go2r7r5x.gif" alt="Hadolint Autocomplete"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that, it could take some seconds for VS Code to index the schema in the first time.&lt;/p&gt;

&lt;p&gt;And that´s it. We have just built an autocomplete feature for &lt;code&gt;hadolint.yaml&lt;/code&gt; files.&lt;/p&gt;

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

&lt;p&gt;JSON Schema provides a simple way to implement auto-completion for JSON and YAML files and is supported out of the box, by many popular editors like Visual Studio, Visual Studio Code, and the Jetbrains family IDEs.&lt;/p&gt;

&lt;p&gt;JSON Schema Store hosts schemas for many popular file formats, but you can also create your own as demonstrated in this article.&lt;/p&gt;

&lt;p&gt;It´s a matter of creating a schema describing your file following the JSON schema specification, and import it in your editor.&lt;/p&gt;

&lt;p&gt;The process of creating a schema for a complex file can be a little painful and boring but in the end, I think the gains in productivity if you work a lot with that file type can pay off.&lt;/p&gt;

&lt;p&gt;I will finish the Hadolint schema and do a PR for the Schema Store, I hope this week.&lt;/p&gt;

&lt;p&gt;Thanks for reading and if you have any questions, feel free to use the Comment Section.&lt;/p&gt;

&lt;p&gt;Also, if you like my content, you can &lt;a href="https://www.buymeacoffee.com/Z1Bu6asGV" rel="noopener noreferrer"&gt;Buy me a coffee&lt;/a&gt; ;)&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>jsonschema</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Load testing your applications with Artillery</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Sat, 11 Apr 2020 10:13:47 +0000</pubDate>
      <link>https://dev.to/brpaz/load-testing-your-applications-with-artillery-4m1p</link>
      <guid>https://dev.to/brpaz/load-testing-your-applications-with-artillery-4m1p</guid>
      <description>&lt;p&gt;In this article, I will show how to use &lt;a href="https://artillery.io/"&gt;Artillery&lt;/a&gt; to load test your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Artillery?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://artillery.io/"&gt;Artillery&lt;/a&gt; is a modern, powerful &amp;amp; easy-to-use solution for load testing and functional testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It´s built with NodeJS and it´s open source.&lt;/p&gt;

&lt;p&gt;Unlike other load testing tools that have complicated GUIs, Artillery is a simple CLI tool, making it very easy to use and to integrate into any CI environment.&lt;/p&gt;

&lt;p&gt;It supports testing applications that use HTTP, Socket.io or Websockets.&lt;/p&gt;

&lt;p&gt;One of the biggest points of Artillery is that it allows defining test scenarios using YAML, making it possible to version the tests together with the application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Artillery can be installed like any other Node package, using NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; artillery 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Doing a quick test
&lt;/h2&gt;

&lt;p&gt;Artillery can be used to do one-off tests to a specific URL in a similar way of tools like &lt;a href="https://httpd.apache.org/docs/2.4/programs/ab.html"&gt;Apache Benchmark&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That is done using the &lt;code&gt;quick&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;artillery quick &lt;span class="nt"&gt;-c&lt;/span&gt; 10 &lt;span class="nt"&gt;-n&lt;/span&gt; 20 https://httpbin.org/get
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will launch 10 virtual users that will do 20 requests each, to the specified URL.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;quick&lt;/code&gt; command supports some other flags to control how the load will be distributed, like &lt;code&gt;rate&lt;/code&gt;, which can be used to define the number of new users per second, or the &lt;code&gt;duration&lt;/code&gt;, which defines a fixed time for the test.&lt;/p&gt;

&lt;p&gt;For example, the following command will run the test during 10s, with 2 new users arriving each second, resulting in around 20 requests in the total.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;artillery quick https://httpbin.org/get &lt;span class="nt"&gt;-r&lt;/span&gt; 2 &lt;span class="nt"&gt;-d&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can combine all these flags, to achieve the desired pattern of load for your test.&lt;/p&gt;

&lt;p&gt;In terms of other options, I found this command to be pretty limited as you can´t configure almost anything more of the request, like defining request headers. It also only allows GET requests.&lt;/p&gt;

&lt;p&gt;So, if you just want to do some one-off test directly from the command line and need a little more flexibility configuring the request, there are probably better tools like Apache Benchmark.&lt;/p&gt;

&lt;p&gt;But the power of Artillery lies in being able to simulate realistic user behavior with scenarios and flows, using declarative YAML files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scnearios and YAML configuration
&lt;/h2&gt;

&lt;p&gt;The most basic test case can be defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://httpbin.org/'&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&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;/get"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;config&lt;/code&gt; section allows defining some generic configuration about the test, like the target URL, the duration of the test and the way users will generate load.&lt;/p&gt;

&lt;p&gt;In this example, we define one phase, which will last 60 seconds with 20 new virtual users (arriving every second (on average)).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;scenarios&lt;/code&gt; section defines the scenarios this test will cover. Each scenario has a flow associated with it, which is a set of steps that each user will do in your application.&lt;/p&gt;

&lt;p&gt;We will see this in more detail, later in this article.&lt;/p&gt;

&lt;p&gt;In this particular case, we have defined one single scenario, with a single step, which is a GET request to the &lt;code&gt;https://httpbin.org/get&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;You can run this test using the &lt;code&gt;run&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;artillery run &amp;lt;path_to_file&amp;gt;.yml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is very similar to the "quick" example, just a request to a specific endpoint. &lt;br&gt;
But unlike the "quick" command, using a configuration allows you to define a lot more params of the request, like HTTP method, headers, cookies, request payload, etc. You can even load your payload dynamically from CSV files.&lt;/p&gt;

&lt;p&gt;Here is a more advanced example demonstrating how to do a POST request with payload data loaded from a CSV file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# path is relative to the location of the test script&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users.csv"&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password"&lt;/span&gt;
  &lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&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;/auth"&lt;/span&gt;
            &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
              &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This test will do a "POST" request to the "/auth" endpoint, with a JSON body containing the fields "username" and "password", which values are loaded from a CSV file "users.csv", from the specified fields.&lt;/p&gt;

&lt;p&gt;You can find out about all the possibilities in the &lt;a href="https://artillery.io/docs/script-reference/"&gt;Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A more complete user flow
&lt;/h2&gt;

&lt;p&gt;We have seen how to define a scenario with a simple flow.&lt;br&gt;
Next, we will see how you can define an entire user journey.&lt;/p&gt;

&lt;p&gt;Considering a typical E-commerce site. You might want to simulate a user flow, starting from the catalog page, selecting a product, and add it to the cart.&lt;/p&gt;

&lt;p&gt;This can be achieved with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;target&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://staging1.local"&lt;/span&gt;
  &lt;span class="na"&gt;phases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60&lt;/span&gt;
      &lt;span class="na"&gt;arrivalRate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keywords.csv"&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keywords"&lt;/span&gt;
&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;buy"&lt;/span&gt;
    &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&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;/search"&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kw={{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;keywords&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;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;$.results[0].id"&lt;/span&gt;
            &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&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;/details/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&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;/cart"&lt;/span&gt;
          &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Some things we haven´t seen before. &lt;/p&gt;

&lt;p&gt;A flow can consist of multiple steps, representing the user journey in the application. Each user spawned by Artillery will execute all the defined steps sequentially.&lt;/p&gt;

&lt;p&gt;In this particular example, The first step is doing a POST request to the "/search" endpoint with a body, containing keywords, loaded from a CSV file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&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;/search"&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kw={{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;keywords&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;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;$.results[0].id"&lt;/span&gt;
            &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The "capture" block allows us to get a value from the response and save it as a variable for later use. You can use "JSONPath", "XPath", "Regex" and more to parse the response and get the value you want.&lt;/p&gt;

&lt;p&gt;For example, to get a URL form an anchor tag in HTML response you could do as following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&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;/some/page"&lt;/span&gt;
    &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a[class^=productLink]"&lt;/span&gt;
        &lt;span class="na"&gt;attr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;href"&lt;/span&gt;
        &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;productUrl"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Back to our E-commerce flow, We will use the captured "id" variable from the "search" step, to go to the "product details" page and to add the product to cart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&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;/details/{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&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;/cart"&lt;/span&gt;
    &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As you can see with a few lines of YAML we can define an entire user flow to test.&lt;/p&gt;

&lt;p&gt;Artillery supports many more functions to work with HTTP requests and responses. You can find then in the &lt;a href="https://artillery.io/docs/http-reference/"&gt;http reference&lt;/a&gt; section of the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple Scenarios
&lt;/h2&gt;

&lt;p&gt;In the previous example, we saw how to define a single scenario with multiple steps.&lt;br&gt;
But you can also define multiple scenarios to simulate users doing different things at your application at the same time.&lt;/p&gt;

&lt;p&gt;Let´s say you want to simulate users searching and buying products at the same time. You can create two different scenarios to test both flows. Note that each user will only execute one of the scenarios. &lt;/p&gt;

&lt;p&gt;The scenario supports a "weight" property which you can use to indicate how likely it is that scenario to be picked by a particular user. &lt;/p&gt;

&lt;p&gt;By default, each scenario will have the same probability. If you define a scenario with weight 2, that scenario is twice as likely to be picked as the one with 1 (which is the default value).&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting success conditions
&lt;/h2&gt;

&lt;p&gt;It´s possible to configure Artillery to return a non-zero exit code if the test run doesn't comply with specified conditions based on a set of parameters like error rate, minimum, maximum and percentile based latency.&lt;/p&gt;

&lt;p&gt;This is really useful in a CI environment as you can make the test fail if it doesn´t meet your performance requirements.&lt;/p&gt;

&lt;p&gt;The following configuration will ensure that at least 95% of requests are executed below 200ms, otherwise, the command will exit with an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ensure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;p95&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Extend Artillery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hooks
&lt;/h3&gt;

&lt;p&gt;Artillery has support for "hooks", which allow for custom JS functions to be executed at certain points during the execution of a scenario.&lt;/p&gt;

&lt;p&gt;The following extension points are available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;beforeScenario&lt;/code&gt; and &lt;code&gt;afterScenario&lt;/code&gt; - called before/after a virtual user executes a scenario&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;beforeRequest&lt;/code&gt; - called before a request is sent. request parameters can be customized here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;afterResponse&lt;/code&gt; - called after a response has been received. The response can be inspected and custom variables can be set here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;function&lt;/code&gt; - which can be inserted as a step at any point in a scenario.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can use these hooks, to log extra information you need or to modify the request of some sort before doing a request. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://artillery.io/blog/using-fakerjs-with-artillery"&gt;this&lt;/a&gt; article shows a very good example of this, by leveraging &lt;a href="https://github.com/marak/Faker.js/"&gt;Faker.js&lt;/a&gt; to generate random data for the tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plugins
&lt;/h3&gt;

&lt;p&gt;Artillery supports custom plugins that you can write using Javascript.&lt;/p&gt;

&lt;p&gt;Artillery has some official plugins like &lt;a href="https://artillery.io/docs/plugin-expectations-assertions/"&gt;artillery-plugin-expect&lt;/a&gt; that can be used to add expectations/assertions to your HTTP scenarios, allowing you to do some basic functional tests together with your load tests or the &lt;a href="https://artillery.io/docs/plugin-publish-metrics/"&gt;artillery-publish-metrics&lt;/a&gt;, which allows sending metrics about your test runs to external services like &lt;a href="https://www.datadoghq.com/"&gt;Datadog&lt;/a&gt;, &lt;a href="https://www.influxdata.com/"&gt;InfluxDB&lt;/a&gt; or &lt;a href="https://github.com/statsd/statsd"&gt;StatsD&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example of the &lt;code&gt;artillery-plugin-expect&lt;/code&gt; plugin in use. See The "expect" section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get pets&lt;/span&gt;
    &lt;span class="na"&gt;flow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&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;/pets"&lt;/span&gt;
          &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;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;$.name"&lt;/span&gt;
              &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;
          &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hasProperty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;results&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;equals&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;Tiki"&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can build your own to add extra functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go distributed with Artillery Pro.
&lt;/h3&gt;

&lt;p&gt;Besides the Open source version, Artillery has a Pro version starting at $199/month.&lt;/p&gt;

&lt;p&gt;The pro version offers deep integration with AWS services and allows us to run distributed load tests using AWS infrastructure, and includes other integrations designed to support testing of modern enterprise applications &amp;amp; modern DevOps workflows.&lt;/p&gt;

&lt;p&gt;The open-source version should be enough for most people, but if your scale is pretty big, and you reached the limit of load that a single machine can generate, these extra features can be valuable.&lt;/p&gt;

&lt;p&gt;You can find more about the Pro plan &lt;a href="https://artillery.io/pro/"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;There are many load testing tools out there, some a lot more popular like &lt;a href="https://jmeter.apache.org/"&gt;Jmeter&lt;/a&gt; or &lt;a href="https://gatling.io/"&gt;Gatling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Where Artillery really shines for me compared with these tools, is the balance between simplicity and ease of use and functionality.&lt;/p&gt;

&lt;p&gt;Being a simple CLI tool makes it very easy to install and use in every environment, from the local developer machine to the CI server.&lt;/p&gt;

&lt;p&gt;It´s built with Node, which makes it a lot lighter these Java-based tools.&lt;/p&gt;

&lt;p&gt;The tests are declarative in YAML files, so they are easily readable by everyone and can be versioned together with the application code. &lt;/p&gt;

&lt;p&gt;Scenarios and flows provide great flexibility and allow you to go beyond testing a single endpoint at a time and test entire user journeys.&lt;/p&gt;

&lt;p&gt;"Success Conditions" helps to keep your performance requirements under control, by integrating with your CI tool and make the build fail if it doesn't meet your performance criteria.&lt;/p&gt;

&lt;p&gt;Hooks and plugins make possible to extend the tool with extra functionality.&lt;/p&gt;

&lt;p&gt;There are no more excuses not to write load tests for your applications!&lt;/p&gt;

</description>
      <category>loadtesting</category>
      <category>testing</category>
      <category>performance</category>
      <category>artillery</category>
    </item>
    <item>
      <title>My 2019 Personal Tech stack for Web Development</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Sun, 01 Dec 2019 12:18:27 +0000</pubDate>
      <link>https://dev.to/brpaz/my-2019-personal-tech-stack-for-web-development-2d5j</link>
      <guid>https://dev.to/brpaz/my-2019-personal-tech-stack-for-web-development-2d5j</guid>
      <description>&lt;p&gt;In this article, I will talk a bit about my personal tech stack and related tools for Web Development.&lt;/p&gt;

&lt;p&gt;But, before starting, let me just do a brief introduction about myself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who am I?
&lt;/h2&gt;

&lt;p&gt;I am Web Engineer, working atm in an E-Commerce/Payment company where I do mostly Backend development, using Golang and Java as core technologies.&lt;/p&gt;

&lt;p&gt;I started programming in 2011 after graduating in Computer Science.&lt;/p&gt;

&lt;p&gt;I was still looking for my real passion, and working in a very small startup as my first job, forced me to do a little bit of everything from frontend to backend and some basic ops, in the old days where HTML, CSS, and jQuery were enough to build a frontend.&lt;/p&gt;

&lt;p&gt;Over the years I specialized more in Backend development, mostly using PHP and Symfony.&lt;/p&gt;

&lt;p&gt;Backend development, software architecture and engineering practices are my main focus and passion, but I want to understand all the areas of the Software Development Life cycle and be able to build and launch any product on my own, "from idea to production". &lt;/p&gt;

&lt;p&gt;That´s what I aim for, and try to balance my learnings to reflect that.&lt;/p&gt;

&lt;p&gt;It´s almost impossible to be a complete expert full-stack dev these days. The tech world is just too broad. but I also don't believe in "Single Language" developers.&lt;/p&gt;

&lt;p&gt;The ability to learn and adapt fast is an essential skill.&lt;/p&gt;

&lt;p&gt;I think the sweet spot is somewhere in between. I identify a lot with the concept of &lt;a href="https://medium.com/quick-code/what-it-is-a-t-shaped-developer-and-why-you-should-be-one-e87293e4bb84" rel="noopener noreferrer"&gt;T-Shaped developer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nowadays, the term full-stack developer is also very subjective as there are many more areas besides the traditional Frontend/Backend.&lt;/p&gt;

&lt;p&gt;I like this definition by Adam Wathan:&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1186388252745457665-42" src="https://platform.twitter.com/embed/Tweet.html?id=1186388252745457665"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1186388252745457665-42');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1186388252745457665&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;And this is the kind of full-stack I want to be.&lt;/p&gt;

&lt;p&gt;In the next sections, I will present some of my favorite technologies and tools for all the areas of Web Development.&lt;/p&gt;

&lt;p&gt;Bear in mind, that I am not an expert on all of these. Far from it. &lt;/p&gt;

&lt;p&gt;The backend technologies and some infrastructure yes, I have deep experience and use them a daily basis. &lt;/p&gt;

&lt;p&gt;Others, it´s more a theoretical knowledge from reading articles and books and small experiments, but I am convinced of the value of these and plan to use them in real projects when I have a chance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backend
&lt;/h2&gt;

&lt;p&gt;So let's start with my main area of expertise, Backend development.&lt;/p&gt;

&lt;p&gt;I have two main languages in my stack. PHP and Golang.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP and Symfony
&lt;/h3&gt;

&lt;p&gt;PHP and Symfony are the technologies that I am more comfortable with, since I worked with them for the most part of my career, from the old PHP 5.3 and Symfony 1.4 to the shiny PHP 7.3 and Symfony 4.&lt;/p&gt;

&lt;p&gt;PHP might not be the most popular and pretty language today, far from it, but it´s a very mature language that has received some great improvements in the past few years. It also has some great libraries and frameworks and a strong community.&lt;/p&gt;

&lt;p&gt;It´s a "boring" language and IMO, having a boring language in your stack, that you can rely upon to build working software fast without thinking too much is a must-have for any developer.&lt;/p&gt;

&lt;p&gt;I wouldn't probably be so keen on PHP without &lt;a href="https://symfony.com/" rel="noopener noreferrer"&gt;Symfony&lt;/a&gt; tough. &lt;/p&gt;

&lt;p&gt;Symfony is an amazing and mature framework that focuses on good OOP design and Design patterns. It has very good documentation and a strong focus on Developer experience and performance.&lt;/p&gt;

&lt;p&gt;If you like OOP, Design patterns and Clean code, Symfony is a joy to work with.&lt;/p&gt;

&lt;p&gt;I am not working with Symfony anymore at work, but I have worked for many years and I can build products very fast with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Golang
&lt;/h3&gt;

&lt;p&gt;I have been working with Golang professionally for more than one year and it´s been a fantastic experience.&lt;/p&gt;

&lt;p&gt;I think it complements PHP really well, as some of the strong points of Go are precisely some of the PHP weakest points like concurrency and long-running processes.&lt;/p&gt;

&lt;p&gt;Go it's incredibly light-weight and strongly typed, which I like. It´s not really an OOP language but has some powerful concepts like interfaces that help to build well structured and modular codebases.&lt;/p&gt;

&lt;p&gt;Goroutines are awesome for building highly concurrent applications.&lt;/p&gt;

&lt;p&gt;Being a compiled language makes it easy to distribute and install, making it one of the best languages ideal for Command-line applications, System Tools, and utilities and microservices and APIs.&lt;/p&gt;

&lt;p&gt;Go took the world of DevOps tooling by storm, being the language of choice for popular software like &lt;a href="http://kubernetes.io" rel="noopener noreferrer"&gt;Kubernetes&lt;/a&gt;, &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; or &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am writing more and more Go and it´s becoming my primary language.&lt;/p&gt;

&lt;p&gt;With Symfony and Go in my stack, I believe I can solve 99% of problems related to Backend development.&lt;/p&gt;

&lt;h3&gt;
  
  
  The future
&lt;/h3&gt;

&lt;p&gt;I will continue using more and more Go, but I will always have Symfony in a special place in my heart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;Typescript&lt;/a&gt; is a language that I have an eye on as it gets more and more popular. &lt;/p&gt;

&lt;p&gt;I have never really done everything with it, but if one day I want to replace PHP as the OOP language in my stack, Typescript is looking like a strong candidate.&lt;/p&gt;

&lt;p&gt;I would also like to play with a more functional language. Scala, Haskell, not sure yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;p&gt;One of my goals for next year is to improve my front-end skills.&lt;/p&gt;

&lt;p&gt;With a new JS framework every week, and me not wanting to become a frontend developer, choosing a stack that is stable and mature, with good backward compatibility records, good documentation, and a big community around it, are very important factors to look into.&lt;/p&gt;

&lt;p&gt;Right now there are two major ecosystems, who met these criteria: &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; and &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;Vue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I choose Vue because I prefer the way it is designed with templates instead of JSX for example and I think it´s easier to understand.&lt;/p&gt;

&lt;p&gt;Vue has a great ecosystem of tools and libraries. &lt;/p&gt;

&lt;p&gt;With Vue itself for Single page applications, &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;NuxtJS&lt;/a&gt; when there is a need for Server-Side rendering and &lt;a href="https://gridsome.org/" rel="noopener noreferrer"&gt;Gridsome&lt;/a&gt; for more static sites, I can build all kinds of frontends.&lt;/p&gt;

&lt;p&gt;I could even hook &lt;a href="https://nativescript-vue.org/" rel="noopener noreferrer"&gt;Nativescript&lt;/a&gt; to build native mobile applications.&lt;/p&gt;

&lt;p&gt;If you prefer the React ecosystem, the same kind of tools exist. &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; instead of Nuxt and &lt;a href="https://www.gatsbyjs.org/" rel="noopener noreferrer"&gt;GatsbyJS&lt;/a&gt; instead of Gridsome.&lt;/p&gt;

&lt;p&gt;Some of the React tools are more mature, like Gatsby compared with Gridsome, but still, I hoping them to catch up.&lt;/p&gt;

&lt;p&gt;A big pain for many backend developers is CSS. I also really suck at design. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; is a great option and I am looking forward to the components of &lt;a href="https://www.tailwindui.com/" rel="noopener noreferrer"&gt;Tailwind UI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Or you could go with classic Bootstrap and buy some themes from sites like &lt;a href="https://themeforest.net/" rel="noopener noreferrer"&gt;Themeforest&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scripting
&lt;/h2&gt;

&lt;p&gt;Every developer needs to do some kind of utility scripts once in a while, from data migration utilities to deployment scripts.&lt;/p&gt;

&lt;p&gt;Python is one of the most popular languages for these kinds of scripts because it´s really simple to learn and comes pre-installed in many systems.&lt;/p&gt;

&lt;p&gt;But Go also fits really nice on this. You can run it locally with a simple "go run" and you can compile it in a single binary with all the bundled dependencies (which is a pain in python), to run on a remote system.&lt;/p&gt;

&lt;p&gt;I can write Python, but since I have much more experience with Go, it´s becoming a natural choice for this kind of scripts. &lt;/p&gt;

&lt;p&gt;With a little help of bash for more basic things ;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Datastores
&lt;/h2&gt;

&lt;p&gt;Almost every application needs to store data in some form. &lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://www.mysql.com/" rel="noopener noreferrer"&gt;MySQL&lt;/a&gt; for Relational data, &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt; for in-memory database, cache, etc and &lt;a href="https://www.elastic.co/" rel="noopener noreferrer"&gt;ElasticSearch&lt;/a&gt; or &lt;a href="https://www.algolia.com/" rel="noopener noreferrer"&gt;Algolia&lt;/a&gt; for full text search.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://firebase.google.com/docs/firestore" rel="noopener noreferrer"&gt;Cloud Firestore&lt;/a&gt; is really interesting when I don't want to worry about setup a database and NoSQL storage is enough. It´s really great for prototypes, small personal apps, and frontend/mobile-focused applications.&lt;/p&gt;

&lt;p&gt;I am starting to look more int some "Cloud-native" solutions like &lt;a href="https://fauna.com/" rel="noopener noreferrer"&gt;FaunaDB&lt;/a&gt;, &lt;a href="https://www.arangodb.com/" rel="noopener noreferrer"&gt;ArangoDB&lt;/a&gt; and &lt;a href="https://www.cockroachlabs.com/get-cockroachdb/" rel="noopener noreferrer"&gt;CockroachDB&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Messaging
&lt;/h2&gt;

&lt;p&gt;There are many use cases where a Message Queue system is useful like long-running processes or communication between different services.&lt;/p&gt;

&lt;p&gt;At my current job, I use &lt;a href="https://kafka.apache.org/" rel="noopener noreferrer"&gt;Apache Kafka&lt;/a&gt; and it works great for complex, event-driven systems.&lt;/p&gt;

&lt;p&gt;For personal projects, in most cases, I want a simpler and managed solution. This is where &lt;a href="https://aws.amazon.com/sqs/" rel="noopener noreferrer"&gt;Amazon SQS&lt;/a&gt; and &lt;a href="https://cloud.google.com/pubsub/docs/" rel="noopener noreferrer"&gt;Google Cloud Cloud Pub/Sub&lt;/a&gt; comes in. They are free for most basic workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Object storage
&lt;/h2&gt;

&lt;p&gt;For storing user uploaded files, &lt;a href="https://cloud.google.com/storage/" rel="noopener noreferrer"&gt;Google Cloud Storage&lt;/a&gt; and &lt;a href="https://www.digitalocean.com/products/spaces/" rel="noopener noreferrer"&gt;DigitalOcean spaces&lt;/a&gt; are two good options.&lt;/p&gt;

&lt;p&gt;Another great service, more specialized in media upload is &lt;a href="https://cloudinary.com/" rel="noopener noreferrer"&gt;Cloudinary&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CMS and BaaS
&lt;/h2&gt;

&lt;p&gt;To provide content editing capabilities without much effort, &lt;a href="https://www.netlifycms.org/" rel="noopener noreferrer"&gt;Netlify CMS&lt;/a&gt; is great for a Git-based workflow and &lt;a href="https://prismic.io/" rel="noopener noreferrer"&gt;Prismic&lt;/a&gt; for an API like workflow.&lt;/p&gt;

&lt;p&gt;For a self-hosted solution, &lt;a href="https://directus.io/" rel="noopener noreferrer"&gt;Directus&lt;/a&gt; or &lt;a href="https://strapi.io/" rel="noopener noreferrer"&gt;Strapi&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Infrastructure
&lt;/h2&gt;

&lt;p&gt;Code that is not in production is not really useful. &lt;/p&gt;

&lt;p&gt;I believe every developer should understand the basic concepts of infrastructure and application deployment. &lt;/p&gt;

&lt;p&gt;I am a big fan of the concepts of "Operate what you build" and "Full-Cycle developers" of Netlfix, where the team that develops a system is also responsible for operating and supporting that system. &lt;/p&gt;

&lt;p&gt;You can read more about it in &lt;a href="https://medium.com/netflix-techblog/full-cycle-developers-at-netflix-a08c31f83249" rel="noopener noreferrer"&gt;this&lt;/a&gt; blog post.&lt;/p&gt;

&lt;p&gt;Cloud services make all this easier for developers by abstracting the low-level complexities of networking, storage, etc.&lt;/p&gt;

&lt;p&gt;For generic all-purpose hosting, &lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt; is my platform of choice. It´s very developer-friendly and provides great services at affordable prices. &lt;/p&gt;

&lt;p&gt;Their services offering is not as rich as bigger players like AWS or Google Cloud, but the introduction of managed Kubernetes clusters and managed databases were a great step forward. &lt;/p&gt;

&lt;p&gt;I am also pretty curious about their future &lt;a href="https://www.digitalocean.com/nanobox" rel="noopener noreferrer"&gt;PaaS&lt;/a&gt; after they have acquired &lt;a href="https://blog.digitalocean.com/digitalocean-acquires-nanobox/" rel="noopener noreferrer"&gt;Nanobox&lt;/a&gt; earlier this year. &lt;/p&gt;

&lt;p&gt;In the cases where DigitalOcean doesn't cover all my needs, I go to &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tools like &lt;a href="https://cloud.google.com/run/" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt;, &lt;a href="https://cloud.google.com/functions/" rel="noopener noreferrer"&gt;Cloud Functions&lt;/a&gt;, &lt;a href="https://cloud.google.com/container-registry/" rel="noopener noreferrer"&gt;Container Registry&lt;/a&gt;, &lt;a href="https://cloud.google.com/pubsub" rel="noopener noreferrer"&gt;Cloud Pub/sub&lt;/a&gt;, &lt;a href="https://cloud.google.com/scheduler/" rel="noopener noreferrer"&gt;Cloud scheduler&lt;/a&gt; and &lt;a href="https://cloud.google.com/firestore/" rel="noopener noreferrer"&gt;Cloud firestore&lt;/a&gt; provides the foundations for building all kinds of applications. &lt;/p&gt;

&lt;p&gt;These tools have a great free tier, allowing building small projects for free.&lt;/p&gt;

&lt;p&gt;For provision infrastructure &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; and &lt;a href="https://www.ansible.com/" rel="noopener noreferrer"&gt;Ansible&lt;/a&gt; work very well.&lt;/p&gt;

&lt;p&gt;When I just need to deploy a frontend or a static site, I use &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.namecheap.com/" rel="noopener noreferrer"&gt;Namecheap&lt;/a&gt; to manage my domains and &lt;a href="https://www.cloudflare.com/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; for DNS and CDN.&lt;/p&gt;

&lt;p&gt;For monitoring, both Google Cloud and DigitalOcean offer good basic monitoring built-in. &lt;/p&gt;

&lt;p&gt;As an alternative &lt;a href="https://www.datadoghq.com/" rel="noopener noreferrer"&gt;Datadog&lt;/a&gt; and &lt;a href="https://sentry.io/welcome/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt; are some great tools.&lt;/p&gt;

&lt;p&gt;I am still looking for a good Logging Solution. &lt;a href="https://logz.io/" rel="noopener noreferrer"&gt;Logz.io&lt;/a&gt; looks nice and it´s based on the ELK stack which is very popular and what I use at work. Their Community plan is also very generous for small projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uptimerobot.com/" rel="noopener noreferrer"&gt;UptimeRobot&lt;/a&gt; and &lt;a href="https://www.atlassian.com/software/opsgenie" rel="noopener noreferrer"&gt;Opsgenie&lt;/a&gt; to guarantee I get notified when a site is down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooling
&lt;/h2&gt;

&lt;p&gt;I am using &lt;a href="https://about.gitlab.com/" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; for private projects and CI/CD, but with the available free plan for private repositories and the introduction of GitHub Actions and Package registry, I might move everything to &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. It´s a tough decision as I love the Product vision of GitLab, but GitHub is still more polished in some areas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codacy.com/" rel="noopener noreferrer"&gt;Codacy&lt;/a&gt; gives me automated code reviews &amp;amp; code analytics and it´s free for 4 users.&lt;/p&gt;

&lt;p&gt;For coding, I use &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VScode&lt;/a&gt;, together with &lt;a href="https://www.jetbrains.com/" rel="noopener noreferrer"&gt;JetBrains&lt;/a&gt; IDEs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; and &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker-Compose&lt;/a&gt; provides a streamligned development envrionment.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.getpostman.com/" rel="noopener noreferrer"&gt;Postman&lt;/a&gt; for testing REST APIs, &lt;a href="https://www.cacher.io/" rel="noopener noreferrer"&gt;Cacher&lt;/a&gt; for storing code snippets, &lt;a href="https://dbeaver.io/" rel="noopener noreferrer"&gt;Dbeaver&lt;/a&gt; for connecting to databases.&lt;/p&gt;




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

&lt;p&gt;I really like my stack and I believe I can build almost anything web-related with it.&lt;/p&gt;

&lt;p&gt;It´s not a static list.&lt;/p&gt;

&lt;p&gt;In tech, everything evolves at a very fast pace.&lt;/p&gt;

&lt;p&gt;It´s important to keep updated and experiment with new tech on a regular basis and my stack will keep evolving based on that.&lt;/p&gt;

&lt;p&gt;But it´s very important to avoid "Hype Driven development".&lt;/p&gt;

&lt;p&gt;Always have some "boring" mature tools in your stack, which allow you to keep building things in a fast and sustainable way.&lt;/p&gt;

&lt;p&gt;In the end, people are what matters, so focus on building useful things that improve people´s lives, independent of the stack!&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxx4bopxyr3vhyv468qm8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fxx4bopxyr3vhyv468qm8.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ourstack</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Make your project README stand out with animated GIFs/SVGs</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Sat, 02 Nov 2019 18:26:12 +0000</pubDate>
      <link>https://dev.to/brpaz/make-your-project-readme-file-stand-out-with-animated-gifs-svgs-4kpe</link>
      <guid>https://dev.to/brpaz/make-your-project-readme-file-stand-out-with-animated-gifs-svgs-4kpe</guid>
      <description>&lt;p&gt;An image is worth a thousand words and sometimes it´s much easier to demonstrate how something works by using images or videos instead of just text.&lt;/p&gt;

&lt;p&gt;A common technique that works very well with GitHub and Markdown, is to use an animated GIF or SVG files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lirantal/dockly"&gt;this&lt;/a&gt; or &lt;a href="https://github.com/brpaz/ulauncher-gitlab"&gt;this&lt;/a&gt; shows this in action.&lt;/p&gt;

&lt;p&gt;Looks cool, doesn't it?&lt;/p&gt;

&lt;p&gt;The first thing a user sees in a GitHub project is its README. A good first impression is essential and can make a difference between keep reading or just close the page and look somewhere else.&lt;/p&gt;

&lt;p&gt;With an animated image, the user can see immediately how the project works like, without having to read the entire README or to download the entire project to test himself locally.&lt;/p&gt;

&lt;p&gt;This is much more engaging experience and personally, If I see a project with this, I am much more likely to keep reading with attention the docs and to use it. &lt;/p&gt;

&lt;p&gt;On the other side, for some kind of projects like web applications, it´s almost always a no go, if I cant find quickly any kind of visual demo. It can be just simple screenshots but I want to see how it looks like.&lt;/p&gt;

&lt;p&gt;I don't want to clone the repo, set it up, just to see if it´s worth visually.&lt;/p&gt;

&lt;p&gt;But how can you create these animations?&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording your terminal
&lt;/h2&gt;

&lt;p&gt;If it´s enough for you to record your terminal window, you can use &lt;a href="https://asciinema.org/"&gt;&lt;strong&gt;asciinema&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://github.com/marionebl/svg-term-cli"&gt;&lt;strong&gt;svg-term-cli&lt;/strong&gt;&lt;/a&gt; for it.&lt;/p&gt;

&lt;p&gt;asciinema is a free and open-source solution for recording terminal sessions and sharing them on the web. &lt;/p&gt;

&lt;p&gt;To install it, follow the &lt;a href="https://asciinema.org/docs/installation"&gt;installation&lt;/a&gt; guide for your operative system.&lt;/p&gt;

&lt;p&gt;Then, open a terminal session and type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;asciinema rec myrecord.cast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start a recording session and every command you type from now on will be recorded. To stop the recording, press &lt;code&gt;CTRL+D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your recording will be saved with the name you specified when running the "rec" command, in this example "myrecord.cast".&lt;/p&gt;

&lt;p&gt;Asciinema allows you to upload your recording to their website, but you would need to include their player in the project README, which doesn't play automatically and forces the user to click and be redirected to the Asciinema website to see it, so we will use another tool, &lt;a href="https://github.com/marionebl/svg-term-cli"&gt;svg-term-cli&lt;/a&gt; to generate a regular SVG file from the Asciinema recording.&lt;/p&gt;

&lt;p&gt;svg-term-cli is a node tool, so you need to have node installed on your system and install it with NPM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g svg-term-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tool can take an Asciinema recording as input and generate an animated SVG file as output, that you can add to your project README file as a regular image.&lt;/p&gt;

&lt;p&gt;To generate an SVG file from our previously recorded session, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat myrecord.cast | svg-term --out myrecord.svg --window
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;svg-term-cli&lt;/code&gt; supports a number of flags that you can use to customize the output image. Please look in the project README for details.&lt;/p&gt;

&lt;p&gt;And voilá. You have an animated SVG with your terminal session that you can include in your projects README file as a regular image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording your entire screen
&lt;/h2&gt;

&lt;p&gt;If you need to record more than your terminal window, you can tools like  &lt;a href="https://github.com/phw/peek"&gt;Peek&lt;/a&gt; for Linux or &lt;a href="https://recordit.co/"&gt;recordit&lt;/a&gt; for MAC or Windows to Record your screen and export as an animated GIF file.&lt;/p&gt;

&lt;p&gt;GIF files can become very big easily, so try to record just the area of the screen you need and by a short amount of time.&lt;/p&gt;

&lt;p&gt;You can then add it to your project README as a regular image.&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>github</category>
      <category>tools</category>
    </item>
    <item>
      <title>Factors to consider when adding third party dependencies to a project</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Sat, 02 Nov 2019 16:23:45 +0000</pubDate>
      <link>https://dev.to/brpaz/factors-to-consider-when-adding-third-party-dependencies-to-a-project-46hf</link>
      <guid>https://dev.to/brpaz/factors-to-consider-when-adding-third-party-dependencies-to-a-project-46hf</guid>
      <description>&lt;p&gt;Avoid reinventing the wheel is a good practice when developing any Software project. The focus should be on adding value to the end-user and not to re-implement that function to sort an array every time.&lt;/p&gt;

&lt;p&gt;Thanks to open-source, there is a huge ecosystem of available libraries, that can solve most of the common problems you might have.&lt;/p&gt;

&lt;p&gt;It can be very tempting for every task, to just look for an existing library that can do it for you and add it as a dependency to your project. Don't reinvent the wheel right? Well, yes, but every extra dependency comes with a cost and it is not something that should be done lightly.&lt;/p&gt;

&lt;p&gt;When adding a third-party dependency, your project is now using code that you don't own. &lt;/p&gt;

&lt;p&gt;What happens if that code has a bug or a security issue? Will it be fixed fast enough? And updates? Are they backward compatible or will break your application every time? Considering you will get any updates at all. The developer might just stop working on the project. Do you understand exactly what that dependency does and how to use it?&lt;/p&gt;

&lt;p&gt;These are all important questions that you have to ask, but even before that, it´s important to understand if you really need it to use an external dependency for the functionality you are trying to implement.&lt;/p&gt;

&lt;p&gt;May be is something you can write in a few lines of code? Or maybe you just need one function of a more complex library. In that case, couldn´t you just copy-paste it to your project without having to add the whole thing?&lt;/p&gt;

&lt;p&gt;Taking the extreme example of NPM packages like &lt;a href="https://www.npmjs.com/package/is-odd"&gt;is-odd&lt;/a&gt; or &lt;a href="https://www.npmjs.com/package/is-even"&gt;is-even&lt;/a&gt;. It´s one line of code of basic math!&lt;/p&gt;

&lt;p&gt;Node community is known for abusing the usage of third party dependencies. You might have heard about the &lt;a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/"&gt;left-pad incident&lt;/a&gt;, where a function of a couple of lines broke half of the internet!&lt;/p&gt;

&lt;p&gt;So, think well before adding an external dependency.&lt;/p&gt;

&lt;p&gt;If you are decided that you really need to rely on an external dependency, it´s very common to find different implementations that solve the same problem. How to choose between them? &lt;/p&gt;

&lt;p&gt;I will enumerate some factors that I consider important in the next sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Popularity
&lt;/h2&gt;

&lt;p&gt;If a library is used by many people and has a big community around it, in general, it can be a good indicator of its quality. &lt;/p&gt;

&lt;p&gt;A common used metric for popularity is the number of GitHub stars.&lt;/p&gt;

&lt;p&gt;But be careful with this. Don't simply consider it as your unique decision factor. A library that was built by a big company or by a well-known developer, can have many more stars (because of more publicity for example), than a one done by a single developer or a smaller org. Or maybe it´s a library that exists at more time. &lt;/p&gt;

&lt;p&gt;This criterion alone doesn't guarantee that is a better choice.&lt;/p&gt;

&lt;p&gt;It´s a good indicator yes, but should be considered together with the other factors that I will talk about next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Activity in the Repository
&lt;/h2&gt;

&lt;p&gt;You don't want to depend on something that it´s not actively maintained anymore. &lt;/p&gt;

&lt;p&gt;The frequency of commits, number of open issues and PRs and the speed that the PRs are merged are important indicators of project health.&lt;/p&gt;

&lt;p&gt;Low activity in the repository can be a signal of worry but it depends on the use case. There are libraries that are so simple, that they don't really need changes every day or month. They just work. &lt;/p&gt;

&lt;p&gt;Still, In most cases, I would avoid using a library that have very few commits in the last couple of months and/or with many open Issues / unmerged PRs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project maturity and versioning strategy
&lt;/h2&gt;

&lt;p&gt;It´s best to depend on something that is stable and has a well-defined release schedule and versioning strategy. &lt;/p&gt;

&lt;p&gt;Projects in the early stages of development might have breaking changes more often and can be riskier to use. The same with projects who don't have a defined versioning strategy like &lt;a href="https://semver.org/"&gt;Semantic Versioning&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;With SemVer you can have a better understanding of what´s have changed from one version to another and if it´s a bug fix or new feature, so you can plan ahead when to update your library version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is the maintainer/core contributors
&lt;/h2&gt;

&lt;p&gt;Another important factor to consider is who is the maintainer(s) of the project. Is it a known developer or a big organization? or just a single developer working in its free time?&lt;/p&gt;

&lt;p&gt;In general, I would say that a project that is behind a big company or with a big number of contributors, can be a safer bet in terms of future maintainability and support, but again, it´s just an indicator, not rocket science.&lt;/p&gt;

&lt;p&gt;This fact was indeed often mentioned when favoring &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; against &lt;a href="https://vuejs.org/"&gt;Vue.js&lt;/a&gt;. React is a Facebook project, unlike Vue, where most of the code was done by a single man. &lt;/p&gt;

&lt;p&gt;Still, the fact is that Vue has matured a lot and it´s one of the most popular JS frameworks right now, even surpassed React in terms of GitHub stars.&lt;/p&gt;

&lt;p&gt;An opposite example is &lt;a href="https://www.polymer-project.org/"&gt;Polymer Project&lt;/a&gt;. A framework created by a big company like Google that never gained any traction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality of Documentation
&lt;/h2&gt;

&lt;p&gt;If you want to use a particular library, you have to understand how it works. &lt;/p&gt;

&lt;p&gt;A library with poor or no documentation, it´s a big red flag for me. &lt;/p&gt;

&lt;p&gt;At very minimum, it should have a README file explaining how to install and with code examples for the main use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test coverage
&lt;/h2&gt;

&lt;p&gt;If a library has very few or no tests, how can you be sure that it will do what you are expecting it to do and that you won´t encounter critical bugs when integrating it into your project?&lt;/p&gt;

&lt;p&gt;It´s really bad to have a bug on your application because of a bug on a third-party library.&lt;/p&gt;

&lt;p&gt;Good test coverage can also show that the developer cares about the quality and maintainability of the software and that it's not just some experimental side project and can be used in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Number of dependencies
&lt;/h2&gt;

&lt;p&gt;You are adding a dependency to your project, but that dependency will probably have their own dependencies. &lt;/p&gt;

&lt;p&gt;This is something that many developers don't pay enough attention to. What are those dependencies? Are those still maintainable and trustworthy? Of course, it will hard to check every dependency but if a library has too many dependencies can be a warning sign.&lt;/p&gt;

&lt;p&gt;In case of doubt, I will always choose the one with fewer dependencies.&lt;/p&gt;




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

&lt;p&gt;Avoid reinventing the wheel is essential and thanks to all the amazing Open Source projects out there, our lives as developers are a lot easier as most of the common problems were already solved by someone else.&lt;/p&gt;

&lt;p&gt;But adding a third-party dependency to a project is a decision that should be well thought, You project now depends on code you don't own or write. What if it has bugs? Will it be fixed soon enough? Will updates break your code? What if the developer just stops working on the library?&lt;/p&gt;

&lt;p&gt;You can mitigate these issues by being rigorous when choosing the dependencies that you will add to your project. Give preference to the ones that are actively developed, have great documentation and test coverage and few extra dependencies.&lt;/p&gt;

&lt;p&gt;A good strategy to minimize the dependency on a library in a project is to create a wrapper around it. This will make it easier to change as your application code will never call the library directly, but the wrapper. If you want to replace that library with another, just change the wrapper to use the new library internally and it´s done.&lt;/p&gt;

&lt;p&gt;Another important thing is to keep your dependencies up to date. Tools like &lt;a href="https://dependabot.com/"&gt;Dependabot&lt;/a&gt; or &lt;a href="https://github.com/renovatebot/renovate"&gt;Renovate&lt;/a&gt; can give a big help with that.&lt;/p&gt;

&lt;p&gt;What about you? Do you have any criteria for choosing a library that I didn't mention? Feel free to comment below.&lt;/p&gt;

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

</description>
      <category>softwareengineering</category>
      <category>dependencymanagement</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Building a basic CI/CD pipeline for a Golang application using GitHub Actions</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Mon, 19 Aug 2019 20:45:42 +0000</pubDate>
      <link>https://dev.to/brpaz/building-a-basic-ci-cd-pipeline-for-a-golang-application-using-github-actions-icj</link>
      <guid>https://dev.to/brpaz/building-a-basic-ci-cd-pipeline-for-a-golang-application-using-github-actions-icj</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com"&gt;GitHub&lt;/a&gt; has announced last week exciting new features for &lt;a href="https://github.com/features/actions"&gt;GitHub Actions&lt;/a&gt;, including built-in support for CI/CD pipelines. &lt;/p&gt;

&lt;p&gt;You can watch the full announcement on &lt;a href="https://www.youtube.com/watch?v=E1OunoCyuhY"&gt;Youtube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is a huge milestone for GitHub and one of the most anticipated features since platforms like &lt;a href="https://about.gitlab.com/product/continuous-integration/"&gt;GitLab&lt;/a&gt; and &lt;a href="https://bitbucket.org/product/features/pipelines"&gt;Bitbucket&lt;/a&gt; already have solutions for this for many time.&lt;/p&gt;

&lt;p&gt;It´s another good example of the tremendous evolution of the company under Microsoft and Nat Friedman leadership.&lt;/p&gt;

&lt;p&gt;GitHub Actions will allow building a complete CI/CD Pipeline, deeply integrated with the GitHub ecosystem, without the need to use a third-party service like Travis CI or Circle CI, following the trend for "All in one" solutions where GitLab is probably the best example.&lt;/p&gt;

&lt;p&gt;The feature is in beta for a limited group of users and is expected to be released for all users in November this year. It will be free for Open source projects and have 2000 free build minutes per month for Private repositories. (by user, not repo).&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://github.com/features/actions/signup"&gt;request early access&lt;/a&gt; now. I already have it and I am writing this post to show what you can do!&lt;/p&gt;

&lt;h2&gt;
  
  
  What we will build
&lt;/h2&gt;

&lt;p&gt;To demonstrate the new features of GitHub Actions, we will build a "Hello world" Golang app with a very basic pipeline that after each Pull Request or push to master branch, will lint our code, run unit tests and generate code coverage report using &lt;a href="https://codecov.io/"&gt;Codecov&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then when a new "tag" is created in the repository, it will create a new GitHub release using &lt;a href="https://goreleaser.com/"&gt;GoReleaser&lt;/a&gt; tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The project code
&lt;/h2&gt;

&lt;p&gt;The example repository is accessible &lt;a href="https://github.com/brpaz/github-actions-demo-go"&gt;here&lt;/a&gt;, Feel free to fork it or just follow along.&lt;/p&gt;

&lt;p&gt;I won't go into many details about the code of the application itself. It´s a standard "Hello world" app that prints the text "Hello GitHub actions" to the standard output.&lt;/p&gt;

&lt;p&gt;Here is the code for the main.go file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/brpaz/go-github-actions/hello"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hello&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Greet&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 is the "Greet" function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;

&lt;span class="c"&gt;// Greet Greets GitHub Actions&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello GitHub Actions"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And the respective unit test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"testing"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestGreetsGitHub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"Hello GitHub Actions"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greet() = %s; want Hello GitHub actions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;

&lt;p&gt;GitHub Actions are based on the concept of Workflows. A workflow is nothing more than a set of jobs and steps that are executed when some condition or event is met. (Ex: a push to the repository, a pull request, a deployment, etc).&lt;/p&gt;

&lt;p&gt;You can have multiple workflows by project, each one responding to a different set of events.&lt;/p&gt;

&lt;p&gt;In our example, we will have two workflows. The "Build" or "Main" workflow which will be triggered when there is a push the master branch or when a PR is created and the "Release" workflow which will run when a new tag is pushed to GitHub, that will create a new release of the application.&lt;/p&gt;

&lt;p&gt;Each Workflow is composed of one or more Jobs. Our "Build" Workflow will have 3 Jobs (Lint, Build and Test) and our "Release" workflow will have a single "release" job.&lt;/p&gt;

&lt;p&gt;Each job is made of steps. For Example, the "Unit Test" job will have steps for checkout the source code, run the tests and generating code coverage report.&lt;/p&gt;

&lt;p&gt;The best part is that you don't have to reinvent the wheel and you can reuse existing actions built by GitHub itself or the community and even just regular Docker images in your steps. &lt;/p&gt;

&lt;p&gt;We will see examples of all of them in the article.&lt;/p&gt;

&lt;p&gt;Workflows are defined in YAML files located in &lt;code&gt;.github/workflows&lt;/code&gt; directory of your repository.&lt;/p&gt;

&lt;p&gt;Each file in the directory represents a different Workflow.&lt;/p&gt;

&lt;p&gt;Here is how our Build workflow looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Test&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;lint&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;Lint&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Go&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-go@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;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.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="s"&gt;Check out code&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@v1&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;Lint Go Code&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;export PATH=$PATH:$(go env GOPATH)/bin # temporary fix. See https://github.com/actions/setup-go/issues/14&lt;/span&gt;
          &lt;span class="s"&gt;go get -u golang.org/x/lint/golint &lt;/span&gt;
          &lt;span class="s"&gt;make lint&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Go&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-go@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;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.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="s"&gt;Check out code&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@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Unit tests.&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make test-coverage&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;Upload Coverage report to CodeCov&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;codecov/codecov-action@v1.0.0&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;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.CODECOV_TOKEN}}&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./coverage.txt&lt;/span&gt;

  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt; 
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Go&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-go@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;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.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="s"&gt;Check out code&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@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We start by defining a name for the workflow and when it will be run.&lt;br&gt;
In our case, we want it to run when there is a push to master or a pull request. There are many events you can listen to. You can read more about it &lt;a href="https://help.github.com/en/articles/configuring-a-workflow#triggering-a-workflow-with-events"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workflow contains 3 jobs, "lint", "test" and "build". &lt;/p&gt;

&lt;p&gt;Let´s give a quick look at the "lint" job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;lint&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;Lint&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Go&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-go@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;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.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="s"&gt;Check out code&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@v1&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;Lint Go Code&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;export PATH=$PATH:$(go env GOPATH)/bin # temporary fix. See https://github.com/actions/setup-go/issues/14&lt;/span&gt;
          &lt;span class="s"&gt;go get -u golang.org/x/lint/golint &lt;/span&gt;
          &lt;span class="s"&gt;make lint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, we specify that we want this job to run on an ubuntu machine. ("runs-on" keyword). &lt;/p&gt;

&lt;p&gt;Actions have support for Linux, Mac, and Windows as well as Docker. In the future, it will be possible to use your own machines also as runners.&lt;/p&gt;

&lt;p&gt;Then, we define the steps that compose our job.&lt;/p&gt;

&lt;p&gt;First thing is to install Go. GitHub already provides an action for it, so let's use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Go&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-go@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;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I think the syntax is pretty explanatory. The &lt;code&gt;with&lt;/code&gt; keyword allows us to specify the arguments required by the action. In this case, the "setup-go" action allows us to specify the go version to use.&lt;/p&gt;

&lt;p&gt;Next step is to check-out the source code. Again we will use a built-in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt; &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out code&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@v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And finally we will install and run &lt;code&gt;golint&lt;/code&gt; tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint Go Code&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;export PATH=$PATH:$(go env GOPATH)/bin&lt;/span&gt;
    &lt;span class="s"&gt;go get -u golang.org/x/lint/golint &lt;/span&gt;
    &lt;span class="s"&gt;make lint&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And that´s it. The rest of the jobs are pretty similar. Let´s take a look to the "Test" job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;test&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;Test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Go&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-go@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;go-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.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="s"&gt;Check out code&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@v1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Unit tests.&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make test-coverage&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;Upload Coverage report to CodeCov&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;codecov/codecov-action@v1.0.0&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;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.CODECOV_TOKEN}}&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./coverage.txt&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The only new thing here is that we are using a third-party &lt;a href="https://github.com/codecov/codecov-action"&gt;action&lt;/a&gt;, in this case, to publish the test coverage report to CodeCov.&lt;/p&gt;

&lt;p&gt;The usage is exactly the same as built-in actions. In here we are also using a new GitHub functionality "secrets" to store our "Codecov token" required by the CodeCov action. You can configure your secrets by accessing to your project settings -&amp;gt; secrets tab.&lt;/p&gt;

&lt;p&gt;You can create your own actions in any language (Just add a Dockerfile) or if you like Typescript you can use their &lt;a href="https://github.com/actions/toolkit"&gt;actions toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And with this, we finished our first workflow ;)&lt;/p&gt;

&lt;p&gt;Let´s create a new branch and do a code change to see the PR workflow in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout -b greet-devto
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now change our "Greet" function to "greet" also Dev.to users:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;"Hello GitHub Actions. Dev.to is awesome"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We also need to update the respective unit test accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestGreetsGitHub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"ello GitHub Actions. Dev.to is awesome"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Greet() = %s; want ello GitHub Actions. Dev.to is awesome"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&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 push the branch and create a Pull Request to the master branch. The "Build" workflow will start immediately.&lt;/p&gt;

&lt;p&gt;The merge will be blocked until the workflow passes and you will be able to see the status directly in the Pull Request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x7qM_q7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1qlqdjtvq7vwsj8afb45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x7qM_q7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1qlqdjtvq7vwsj8afb45.png" alt="PR Status checks"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember we have added Codecov integration? With one line of code in the workflow, we get full integration with Codecov with PR status checks and Coverage report as a PR comment:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nnIrVf4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ispe3mcllj37fzgzkwfl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nnIrVf4a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ispe3mcllj37fzgzkwfl.png" alt="Codecov report on PR"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The release workflow
&lt;/h2&gt;

&lt;p&gt;It´s time to create our "Release" Workflow". Each workflow is a separate file, so we will create &lt;code&gt;.github/workflows/release.yml&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;Release&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v*&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;release&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;Release on GitHub&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out code&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@v1&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;Validates GO releaser config&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker://goreleaser/goreleaser:latest&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;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check&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;Create release on GitHub&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker://goreleaser/goreleaser:latest&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;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.GITHUB_TOKEN}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We specify that we only want to trigger it on newly created tags and we define a "release" job. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note: The &lt;code&gt;on&lt;/code&gt; condition seems to have some issues. For example, when I push a tag it´s also running the build workflow. Remember Actions is still beta, so take that into account.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The job will check-out the code and use &lt;a href="https://goreleaser.com/"&gt;GoReleaser&lt;/a&gt; official docker image to do all the work.&lt;/p&gt;

&lt;p&gt;When using docker it´s possible to define the "args" and the "entrypoint" of the container. In this case, we will use the default entrypoint, but define a different argument on the "Validate" and "Create Release" steps.&lt;/p&gt;

&lt;p&gt;We also specify the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; environment variable required by Go Releaser to create our release on GitHub. This variable will be passed to the container. Note that &lt;code&gt;secrets.GITHUB_TOKEN&lt;/code&gt; variable is injected automatically by the Actions platform, so no need to create it ourselves.&lt;/p&gt;

&lt;p&gt;If you create a tag and push to the repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git tag v0.1.0
git push --tags
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A new release will be created on GitHub with the application artifacts and Changelog generated by Go Releaser tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nFIlePNv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bram0f07yr8xi07lwmes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nFIlePNv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bram0f07yr8xi07lwmes.png" alt="Release"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And we have our first pipeline built with GitHub actions. ;)&lt;/p&gt;

&lt;p&gt;Very basic example, but I think enough to give you a good idea about how it works.&lt;/p&gt;




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

&lt;p&gt;I am very impressed with the way GitHub Actions work right now and looking forward to the final release.&lt;/p&gt;

&lt;p&gt;I believe GitLab is still superior for more advanced use cases as it supports for example manual approvals and parametrized builds, essential features for the enterprise, but we can´t forget that GitHub Actions is still in beta and I guess these features will come sooner or later. &lt;/p&gt;

&lt;p&gt;Actions also have some nice features that GitLab doesn't, as Matrix builds.&lt;/p&gt;

&lt;p&gt;The features that Actions have currently, should be more than enough for 90% of projects and I believe will be huge for Open source and personal projects.&lt;/p&gt;

&lt;p&gt;And with all the GitHub community building all kinds of open-source actions, we can expect amazing things.&lt;/p&gt;

&lt;p&gt;With the 3 major Git hosting providers supporting CI Pipelines, with Jenkins still very popular in enterprise and some new more specialized tools like &lt;a href="https://codefresh.io/"&gt;Codefresh&lt;/a&gt;, I am curious about the future of more traditional CI only platforms like Travis or Circle CI.&lt;/p&gt;

&lt;p&gt;Exciting times in this space for sure.&lt;/p&gt;

&lt;p&gt;Thanks for reading and please if you had the chance to try the beta, give your feedback in the comment section.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/features/actions"&gt;Features • GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.github.com/en/articles/about-github-actions"&gt;About GitHub Actions - GitHub Help&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>cicd</category>
      <category>githubactions</category>
      <category>devops</category>
    </item>
    <item>
      <title>Setup a local DNS server for your projects on Linux with Dnsmasq</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Fri, 16 Aug 2019 19:27:34 +0000</pubDate>
      <link>https://dev.to/brpaz/setup-a-local-dns-server-for-your-projects-on-linux-with-dnsmasq-blm</link>
      <guid>https://dev.to/brpaz/setup-a-local-dns-server-for-your-projects-on-linux-with-dnsmasq-blm</guid>
      <description>&lt;p&gt;In this article, I will explain how to setup a local DNS server on Linux.&lt;/p&gt;

&lt;p&gt;I will use Dnsmasq, a lightweight DNS server which comes pre-installed in Operating Systems like Ubuntu or Fedora.&lt;/p&gt;

&lt;p&gt;All the instructions on this article are based on a Fedora 30 installation. The concepts will be the same for any other *Nix based OS, but the install commands or locations of some files might differ a little.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why setup your own DNS server?
&lt;/h2&gt;

&lt;p&gt;There are many reasons why you might want to setup your local DNS server&lt;br&gt;
like Caching, but for me, one of the most useful reasons is to resolve local development domains automatically to your localhost.&lt;/p&gt;

&lt;p&gt;For simple projects, using "localhost" and a "port", will probably be enough, but for more complex projects where you need to have multiple applications running at the same time (Ex: A Microservices oriented project), it will start to become more complicated to remember all the ports and to avoid conflicts, so a domain-based approach is easier.&lt;/p&gt;

&lt;p&gt;There are also some cases where your business logic depends on the domain of the application. &lt;/p&gt;

&lt;p&gt;An application where each user logs-in via a subdomain or when you have different domains per country and the content is loaded based on that domain for example.&lt;/p&gt;

&lt;p&gt;In these cases, it's essential to have a "real" domain on your local environment to mimic the expected behavior in production.&lt;/p&gt;

&lt;p&gt;A common approach to this is to add your domains to the &lt;code&gt;hosts&lt;/code&gt; file of your system.&lt;br&gt;
While this works fine, it is not very scalable as you need to add each new domain manually to the file and it doesn't support wildcard subdomains for example.&lt;/p&gt;

&lt;p&gt;A local DNS server can make this a one-time setup.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installing and configuring Dnsmasq
&lt;/h2&gt;

&lt;p&gt;Dnsmasq comes pre-installed in Ubuntu and Fedora. In Ubuntu is not really &lt;code&gt;dnsmasq&lt;/code&gt; but &lt;code&gt;dnsmasq-base&lt;/code&gt;, a stripped version of DNSmasq that is tightly integrated with the system Network manager. that is enough for our needs.&lt;/p&gt;

&lt;p&gt;For other OSes you might need to check the respective OS documentation, but if not already installed, you should be able to install it using the OS default package manager.&lt;/p&gt;

&lt;p&gt;After installing Dnsmasq you need to enable "NetworkManager" to use it as the default DNS server.&lt;/p&gt;

&lt;p&gt;Create a new file &lt;code&gt;/etc/NetworkManager/conf.d/dnsmasq.conf&lt;/code&gt; and add the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[main]
dns=dnsmasq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the "NetworkManager" service to persist your changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart NetworkManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now check the &lt;code&gt;/etc/resolv.conf&lt;/code&gt; and check if there is an entry for nameserver &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add your local domains to Dnsmasq
&lt;/h2&gt;

&lt;p&gt;The next step is to configure your DNS zones for your domains.&lt;/p&gt;

&lt;p&gt;You can specify as many domains as you want, but personally, I am using "lh" (localhost) TLD for all my local domains.&lt;/p&gt;

&lt;p&gt;Go to &lt;code&gt;/etc/NetworkManager/dnsmasq.d&lt;/code&gt; and create a new file with the name of your TLD, in my case &lt;code&gt;lh.conf&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;address=/lh/127.0.0.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, if you have installed full Dnsmasq and not using the one that comes with NetworkManager, the configuration should be added to &lt;code&gt;/etc/dnsmasq.d/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This instructs Dnsmasq to resolve any query to the &lt;code&gt;lh&lt;/code&gt; TLD to your local machine.&lt;br&gt;
You don't have to always point to &lt;code&gt;127.0.0.1&lt;/code&gt; tough. If you are using VMs, for example, you cant point to your VM IP address.&lt;/p&gt;

&lt;p&gt;A good example of this use case is when using &lt;a href="https://github.com/kubernetes/minikube"&gt;minikube&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have created an &lt;code&gt;mkube&lt;/code&gt; entry in Dnsmasq to resolve to the Minikube VM IP Address, so I can create an &lt;code&gt;app1.mkube&lt;/code&gt;, &lt;code&gt;app2.mkube&lt;/code&gt; and they will reach Minikube automatically.&lt;/p&gt;

&lt;p&gt;Finally, you need to restart the services to reload your changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart dnsmasq NetworkManager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check everything is working, open your terminal and type &lt;code&gt;dig test.lh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You should see that it resolves to &lt;code&gt;127.0.0.1&lt;/code&gt;. You can test any other subdomains and they all should resolve to your machine.&lt;/p&gt;

&lt;p&gt;And voilá, you have setup a very simple DNS Server. No need to worry about ports or host file again. Any *.lh request will resolve to your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;The next steps would be setup an HTTP server at your localhost to handle your requests and proxy to the correct application based on the domain name.&lt;/p&gt;

&lt;p&gt;If you are using Docker, I recommend giving &lt;a href="https://traefik.io/"&gt;Traefik&lt;/a&gt; a try.&lt;/p&gt;

&lt;p&gt;Install Traefik on your machine listening on port 80. Traefik will listen to Docker events and automatically reload its configuration when a container status changes. &lt;/p&gt;

&lt;p&gt;You can set a hostname for the application and Traefik will do the necessary mapping, so for example, &lt;code&gt;my-app.docker.lh&lt;/code&gt; will be proxied automatically to the correct internal port of the container.&lt;/p&gt;

&lt;p&gt;You might need to setup some labels on your container, nothing more. Traefik and Dnsmasq will handle the rest.&lt;/p&gt;

&lt;p&gt;This setup is out of the scope of this article. I might write another article about this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note for Ubuntu users
&lt;/h2&gt;

&lt;p&gt;If you are using Ubuntu please check &lt;a href="https://askubuntu.com/questions/1029882/how-can-i-set-up-local-wildcard-127-0-0-1-domain-resolution-on-18-04?rq=1"&gt;this&lt;/a&gt; question on Stack Overflow.&lt;/p&gt;

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

&lt;p&gt;For smaller or single application projects, a port-based approach is perfectly fine and there is no reason to not stick with it.&lt;/p&gt;

&lt;p&gt;But if you work on many projects at the same time, projects with complex microservices architecture, or when your application have a logic based on the domain or subdomain, Dnsmasq provides a very simple way of managing your domains without having to manually deal with the &lt;code&gt;hosts&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fedoramagazine.org/using-the-networkmanagers-dnsmasq-plugin/"&gt;Using the NetworkManager's DNSMasq plugin - Fedora Magazine&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>linux</category>
      <category>networking</category>
      <category>dns</category>
      <category>dnsmasq</category>
    </item>
    <item>
      <title>What is your´s worst experience with recruiters?</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Sun, 07 Jul 2019 11:07:27 +0000</pubDate>
      <link>https://dev.to/brpaz/what-is-your-s-worst-experience-with-recruiters-2ld1</link>
      <guid>https://dev.to/brpaz/what-is-your-s-worst-experience-with-recruiters-2ld1</guid>
      <description>&lt;p&gt;If you work in tech and have a Linkedin account, the probability that you receive daily messages from recruiters is pretty high. &lt;/p&gt;

&lt;p&gt;While a few might be interesting and realistic proposals, I would say most of them could be considered close to SPAM and show a lack of preparation and understanding of the field. (Like saying that are looking for Java devs, for someone who has Javascript in their profile).&lt;/p&gt;

&lt;p&gt;This morning, I was reading my Twitter feed and saw this tweet:&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;
      &lt;div class="ltag__twitter-tweet__media"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6-6hPgn_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/D-ot4-hW4AA-ChG.jpg" alt="unknown tweet media content"&gt;
      &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--TREqIxmr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/777453433624428544/uadOkZCL_normal.jpg" alt="Daniele Bottillo profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Daniele Bottillo
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="comment-mentioned-user" href="https://dev.to/dbottillo"&gt;@dbottillo&lt;/a&gt;

      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P4t6ys1m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Recruiter also in GitHub opening PRs on my personal open source project, that's next level for me. 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      13:55 PM - 04 Jul 2019
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1146779523310084096" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WwRENZp4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1146779523310084096" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PFD0MJBa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1146779523310084096" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6wx1BHu3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


&lt;p&gt;A guy who was contacted by a recruiter via a Pull Request in one of his Open source repos!&lt;/p&gt;

&lt;p&gt;It seems that recruiters are getting more and more "creative" and start using all the possible means just to get your attention! &lt;/p&gt;

&lt;p&gt;While I understand creativity is important to get noticed in a very crowded field like tech, some actions might be too much invasive.&lt;/p&gt;

&lt;p&gt;This inspired me to write this post and to talk about which was probably my worst experience ever with a recruiter.&lt;/p&gt;

&lt;p&gt;Some years ago, It was another normal working day at my company when the receptionist came to me saying I had a phone call from someone saying claiming to be from the University where I graduated some years ago.&lt;/p&gt;

&lt;p&gt;I was very surprised but went to the phone. &lt;/p&gt;

&lt;p&gt;When I pick it up, the guy starts talking that he had an opportunity for me and if I want to schedule an interview and so on. &lt;/p&gt;

&lt;p&gt;I ended up not understanding If he really worked at the university or not, but I believe it was a just bait to get my attention. He probably got that info as well as the company info by looking at my Linkedin profile. &lt;/p&gt;

&lt;p&gt;He also gave me a name that I couldn't find anywhere on Linkedin, so I believe it was fake too.&lt;/p&gt;

&lt;p&gt;And by his voice, seemed very anxious and not really secure of himself.&lt;/p&gt;

&lt;p&gt;What can lead to someone to have this kind of behavior? Calling to your work with a bunch of lies just to grab your attention?&lt;/p&gt;

&lt;p&gt;I believe there are good and professional recruiters out there and that they have an important role, but I think the image of the field is getting very damaged by examples like these.&lt;/p&gt;

&lt;p&gt;So, What are your experiences with recruiters?&lt;/p&gt;

&lt;p&gt;If you work in the recruiting field and are reading this, please also feel free to share your opinion on the topic.&lt;/p&gt;

</description>
      <category>recruiting</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Ensure the quality of your Docker Images with these cool tools</title>
      <dc:creator>Bruno Paz</dc:creator>
      <pubDate>Mon, 01 Jul 2019 20:32:10 +0000</pubDate>
      <link>https://dev.to/brpaz/ensure-the-quality-of-your-docker-images-with-these-cool-tools-1bh7</link>
      <guid>https://dev.to/brpaz/ensure-the-quality-of-your-docker-images-with-these-cool-tools-1bh7</guid>
      <description>&lt;p&gt;When we talk about software testing, most of the time it's about testing the application code in order to guarantee it safeties our defined code quality metrics and functional/business requirements. &lt;/p&gt;

&lt;p&gt;The same concepts like Unit Testing or Integration Testing can also be applied for infrastructure related code.&lt;/p&gt;

&lt;p&gt;Testing VMs and Bare metal systems can be complex and very time and resource consuming, but with containers that process is much easier.&lt;/p&gt;

&lt;p&gt;Next, I will present some tools that you can use to ensure the quality of your Docker images.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hadolint
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/hadolint/hadolint"&gt;Hadolint&lt;/a&gt; is a linter for Dockerfiles. &lt;/p&gt;

&lt;p&gt;It can be used to ensure that you Dockerfile doesn't contain structural errors and follows the official &lt;a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/"&gt;best practices&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It also uses &lt;a href="https://github.com/koalaman/shellcheck"&gt;ShellCheck&lt;/a&gt; under the hood, which is a static analysis tool for shell scripts, to validate the shell code present in your Dockerfile "RUN" instructions, which is pretty neat.&lt;/p&gt;

&lt;p&gt;Having a well written Dockerfile is the first step to a quality Image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Container Structure Test
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/GoogleContainerTools/container-structure-test"&gt;Container structure test&lt;/a&gt; is a tool developed by Google to validate the structure of your image. It can be seen as a Unit Test framework for containers.&lt;/p&gt;

&lt;p&gt;These tests can be used to check the output of commands in an image, as well as verify metadata and contents of the filesystem.&lt;/p&gt;

&lt;p&gt;You define your tests in a YAML file and then run the tool against an existing image locally or it some registry.&lt;/p&gt;

&lt;p&gt;A simple metadata test, that checks the existing of environment variables, labels, ports and other can be defined as such:&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;metadataTest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;baz&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;com.example.vendor'&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ACME&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Incorporated'&lt;/span&gt;
  &lt;span class="na"&gt;exposedPorts&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;8080"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2345"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;volumes&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;/test"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
  &lt;span class="na"&gt;cmd&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;/bin/bash"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;workdir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/app"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Goss and DGoss
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/aelsabbahy/goss"&gt;Goss&lt;/a&gt; is another YAML based tool for validating a server’s configuration. It's built in Go and can be used to test all kinds of systems from Virtual Machines to containers. &lt;a href="https://github.com/aelsabbahy/goss/tree/master/extras/dgoss"&gt;Dgoss&lt;/a&gt; its a wrapper that facilitates the usage of Goss with containers.&lt;/p&gt;

&lt;p&gt;Here is an example of a test definition using Goss:&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;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;tcp:22:&lt;/span&gt;
    &lt;span class="s"&gt;listening&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0&lt;/span&gt;
  &lt;span class="s"&gt;tcp6:22:&lt;/span&gt;
    &lt;span class="s"&gt;listening&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="s"&gt;ip&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;::'&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sshd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;running&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sshd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;uid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;74&lt;/span&gt;
    &lt;span class="na"&gt;gid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;74&lt;/span&gt;
    &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sshd&lt;/span&gt;
    &lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/empty/sshd&lt;/span&gt;
    &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/sbin/nologin&lt;/span&gt;
&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sshd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;gid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;74&lt;/span&gt;
&lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sshd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;running&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see its really simple to understand.&lt;/p&gt;

&lt;p&gt;It is very similar to "Container structure test" but with a bigger set of functions that allow you to verify stuff like services, users, packages, groups, and even HTTP endpoints. You can probably do that by writing your own commands, using Container Structure test, but goss provides these out of the box.&lt;/p&gt;

&lt;p&gt;I think both are good options. I haven't tried Goss yet in practice. Experiment and see what you like more ;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Kitchen CI
&lt;/h2&gt;

&lt;p&gt;One of the popular tools to test infrastructure code is &lt;a href="https://kitchen.ci/"&gt;Kitchen CI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Kitchen provides a test harness to execute infrastructure code on one or more platforms in isolation.&lt;/p&gt;

&lt;p&gt;A driver plugin architecture is used to run code on various cloud providers and virtualization technologies such as Vagrant, Amazon EC2, and Docker. &lt;/p&gt;

&lt;p&gt;It supports many testing frameworks like &lt;a href="https://www.inspec.io/"&gt;InSpec&lt;/a&gt; or &lt;a href="https://serverspec.org/"&gt;Serverspec&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's Ruby based and a lot more complex than Goss or Structure Test. It also requires many more dependencies. Nevertheless its a very powerful tool that you might want to check out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Testing with Clair
&lt;/h2&gt;

&lt;p&gt;A very important aspect that sometimes is forgotten when working with containers is security. Docker images still have an OS like Ubuntu or Alpine under the hood which might have software packages with known security vulnerabilities that need to be monitored and patched.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/coreos/clair"&gt;Clair&lt;/a&gt; is an open source project that helps to find these vulnerabilities in your docker images.&lt;/p&gt;

&lt;p&gt;It contains a database that is updated at regular intervals so it can find the most recently discovered issues.&lt;/p&gt;

&lt;p&gt;You can easily integrate it into your CI Pipeline to be notified on any vulnerability.&lt;/p&gt;

&lt;p&gt;Recently I also discovered &lt;a href="https://anchore.com/"&gt;Anchore&lt;/a&gt; which is very similar but that besides its open source solution also provides an Enterprise solution with Dashboards and other things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep your image sizes in control with Dive
&lt;/h2&gt;

&lt;p&gt;Having small and optimized images is very important in order to have faster builds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/wagoodman/dive"&gt;Dive&lt;/a&gt; is a very cool tool that allows you to explore a docker image, see its layers contents, and more.&lt;/p&gt;

&lt;p&gt;It can help a lot to understand how your image layers are organized and find ways to shrink the size of your final image.&lt;/p&gt;

&lt;p&gt;Even cooler, is that you can run it on your CI system and configure it to fail the build based on some metrics related to the size of the image.&lt;/p&gt;




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

&lt;p&gt;Testing your infrastructure code besides your application code is very important to avoid unexpected problems. &lt;/p&gt;

&lt;p&gt;In more traditional systems it´s not always easy and it can have a high cost in terms of time and resources. &lt;/p&gt;

&lt;p&gt;With Docker and these tools, it's really simple and fast to implement some basic tests to guarantee that the required packages are installed, the specified ports are listening and the needed services are running. &lt;/p&gt;

&lt;p&gt;Having these tests can give you extra confidence that the system as a whole will work as expected.&lt;/p&gt;




&lt;p&gt;Do you know any other cool tool that I haven't mentioned in the article? Feel free to share.&lt;/p&gt;

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

</description>
      <category>testing</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
