<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Jorge Alberto Díaz Orozco (Akiel)</title>
    <description>The latest articles on Forem by Jorge Alberto Díaz Orozco (Akiel) (@jadolg).</description>
    <link>https://forem.com/jadolg</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%2F23687%2F08e1343e-250c-4569-ae0c-d2ad5f7b8c9c.jpg</url>
      <title>Forem: Jorge Alberto Díaz Orozco (Akiel)</title>
      <link>https://forem.com/jadolg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jadolg"/>
    <language>en</language>
    <item>
      <title>Downloading Docker images where DockerHub won't work</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Sun, 04 Jan 2026 19:48:44 +0000</pubDate>
      <link>https://forem.com/jadolg/downloading-docker-images-where-dockerhub-wont-work-35k8</link>
      <guid>https://forem.com/jadolg/downloading-docker-images-where-dockerhub-wont-work-35k8</guid>
      <description>&lt;h2&gt;
  
  
  Why would you want to do this?
&lt;/h2&gt;

&lt;p&gt;Back in 2016 I really wanted to start learning Docker. By the time It had already become a must-have in every developer toolbox. The promises were impressive and as a Linux nerd I was loving what I was reading about it. The problem: I was living in Cuba.&lt;/p&gt;

&lt;p&gt;Cuba, among other countries, is actively blocked from downloading Docker images due to export restrictions. You can't even download what comes for free everywhere. &lt;/p&gt;

&lt;p&gt;Not having access to a VPS or VPN that could mask my location at the time, my only alternative was relying on images downloaded by friends and saved using &lt;code&gt;docker save&lt;/code&gt;, importing them in my docker environment using &lt;code&gt;docker load&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Eventually my position in life shifted and I no longer live in Cuba but many developers just like me still struggle to get access to resources many of you in the other side of the world give for granted. Because of that, I decided to do my part to help my fellow Cuban developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  So how do I use this?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Directly from the website
&lt;/h3&gt;

&lt;p&gt;An instance of my service is deployed at &lt;a href="https://dockerimagesave.akiel.dev/" rel="noopener noreferrer"&gt;https://dockerimagesave.akiel.dev/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgv6da5eb2wz31sgmzf9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbgv6da5eb2wz31sgmzf9.png" alt=" " width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Write the name and tag of the image you want to download and hit the &lt;strong&gt;Download&lt;/strong&gt; button. A file will be eventually downloaded that you can import to your local Docker with &lt;code&gt;docker load -i myimage_tag.tar.gz&lt;/code&gt; and you can share with your friends and colleagues.&lt;/p&gt;

&lt;h3&gt;
  
  
  The nerd way
&lt;/h3&gt;

&lt;p&gt;Do you want to automate your way in? No problems, just use wget/curl/whatever rocks your terminal.&lt;br&gt;
Here's an example using wget to download and load the &lt;strong&gt;ubuntu:25.04&lt;/strong&gt; image in which I included some retries "just in case":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;--tries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5 &lt;span class="nt"&gt;--waitretry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nt"&gt;--content-disposition&lt;/span&gt; &lt;span class="s2"&gt;"https://dockerimagesave.akiel.dev/image?name=ubuntu:25.04"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; docker load &lt;span class="nt"&gt;-i&lt;/span&gt; ubuntu_25_04.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feel free o script around.&lt;/p&gt;

&lt;h2&gt;
  
  
  I want to help others
&lt;/h2&gt;

&lt;p&gt;Great to hear! I'm only one person with limited resources. Setting up a server so your friends can use it is very simple. Just go to &lt;a href="https://github.com/jadolg/DockerImageSave" rel="noopener noreferrer"&gt;https://github.com/jadolg/DockerImageSave&lt;/a&gt; and follow the instructions to deploy using docker compose.&lt;/p&gt;

&lt;p&gt;Wanna make the service better? PRs are open and I'm waiting for your code. Please make sure you write tests for the new code and also that it passes the already existing ones.&lt;/p&gt;

&lt;p&gt;Another good idea to help is to participate in politics. This service fixes a problem that should not exist. It's completely artificial and fabricated by people trying to make other people's life harder. I would love to discontinue this service someday because the world is better.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit of unimportant history
&lt;/h2&gt;

&lt;p&gt;The service itself is somewhat old. I wrote the first version around 6-7 years ago and just last week decided it was time for V2. You might find traces of V1 still in the wild. A much harder to use service that required a client application. That version is no longer maintained nor deployed anywhere as far as I know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;If you made it this far, thanks for reading. That already tells me you care about building things and about making technology accessible to the people who want to learn and use it.&lt;/p&gt;

&lt;p&gt;This project started as a very personal workaround for a very real limitation. Revisiting it years later reminded me that small tools can still make a difference. Sometimes all it takes is removing one unnecessary barrier so someone can experiment, learn, or move forward with their work.&lt;/p&gt;

&lt;p&gt;You do not need to use this service yourself to see its value. If it helps a friend, a colleague, or someone you have never met get unstuck, then it has done its job. If it motivates you to self host it, improve it, or build something similar, even better.&lt;/p&gt;

&lt;p&gt;I sincerely hope we reach a point where tools like this are no longer needed because access is no longer restricted by geography or politics. Until then, I am happy to keep this running and to share it with anyone who finds it useful.&lt;/p&gt;

&lt;p&gt;Thanks for reading, and happy hacking.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>cuba</category>
      <category>censorship</category>
    </item>
    <item>
      <title>Keep Porkbun DNS Records Updated Automatically with Your Current IP</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Fri, 20 Jun 2025 14:40:16 +0000</pubDate>
      <link>https://forem.com/jadolg/keep-porkbun-dns-records-updated-automatically-with-your-current-ip-3539</link>
      <guid>https://forem.com/jadolg/keep-porkbun-dns-records-updated-automatically-with-your-current-ip-3539</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;I love homelabbing. If the term is new to you, it's basically building your own computers/network lab at home. Usually with consumer hardware or old and loud professional servers. Curious? Go visit &lt;a href="http://reddit.com/r/homelab/" rel="noopener noreferrer"&gt;http://reddit.com/r/homelab/&lt;/a&gt; for addictive material that will make you want one.&lt;/p&gt;

&lt;p&gt;My homelab runs services of all kinds, Kubernetes clusters, and experiments I make to study and prepare myself for work plus several things my family and friends actually use.&lt;/p&gt;

&lt;p&gt;I do have a problem though: Every time my home router restarts or re-negotiates internet connection, my &lt;strong&gt;public IP address&lt;/strong&gt; changes. This is very common for home internet connections and there's plenty of software to keep your records updated but, as always, I wanted something special.&lt;/p&gt;

&lt;h3&gt;
  
  
  My requirements
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;All the DDNS software I could find for &lt;strong&gt;Porkbun&lt;/strong&gt; which is my current DNS provider is written in Python and not precisely well packaged. I need something easy to install and run that I can place anywhere with the minimum amount of dependencies needed. &lt;/li&gt;
&lt;li&gt;I had no good way to monitor what's happening. Let's say I want to know if the process is not working well or if my credentials are not working, or just simply knowing when an update happened. Basically, I need metrics.&lt;/li&gt;
&lt;li&gt;All the software I could find supports only one account and I didn't want to repeat the process for the several accounts I manage for my different labs. I need support for multiple accounts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting my hands dirty
&lt;/h2&gt;

&lt;p&gt;I think the first thing I liked about programming in Go is how portable the result is. I can just go ahead and focus on writing my software and I know it's probably going to work almost everywhere. I do like Python too, but, let's face it: it's just harder to distribute and to keep all the dependencies happy for applications. With Go you can just strip almost all system dependencies and package only your application.&lt;/p&gt;

&lt;p&gt;So, I decided to go ahead and write my own Porkbun DDNS client in Go.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠 Try it out
&lt;/h3&gt;

&lt;p&gt;The tool is open-source and available on GitHub:&lt;br&gt;
👉 &lt;a href="https://github.com/jadolg/porkbun-ddns" rel="noopener noreferrer"&gt;jadolg/porkbun-ddns&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Multiple credentials are configurable so you can update records for multiple accounts.&lt;/li&gt;
&lt;li&gt;You are not restricted to only one record to update.&lt;/li&gt;
&lt;li&gt;It has IPv6 support.&lt;/li&gt;
&lt;li&gt;You can expose metrics from it and build monitoring and alerting based on them:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dq3bmmslottmzoabgte.png" alt="Grafana Dashboard" width="800" height="384"&gt;
&lt;/li&gt;
&lt;li&gt;Timeouts and update intervals are configurable for flexibility.&lt;/li&gt;
&lt;li&gt;Extremely easy to deploy either just downloading the binary and starting it as a service or using &lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; or &lt;a href="https://snapcraft.io/" rel="noopener noreferrer"&gt;snap&lt;/a&gt; with more options coming in the future.&lt;/li&gt;
&lt;li&gt;All settings are controlled via this simple YAML file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;update_interval_minutes&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;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;20s&lt;/span&gt;

&lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prod&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;porkbun_api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeme&lt;/span&gt;
    &lt;span class="na"&gt;porkbun_secret_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeme&lt;/span&gt;
  &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;porkbun_api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeme&lt;/span&gt;
    &lt;span class="na"&gt;porkbun_secret_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;changeme&lt;/span&gt;

&lt;span class="na"&gt;records&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;www&lt;/span&gt;
    &lt;span class="na"&gt;ipv6&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;ipv4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example.com&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;blog&lt;/span&gt;
    &lt;span class="na"&gt;ipv4&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;

&lt;span class="na"&gt;metrics&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="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7879&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;I know this sounds like I’m advertising my own tool—because I am. But I built it to solve real problems I think many of you face too. I love writing tools that are useful, and I hope this one helps you as much as it’s helped me.&lt;/p&gt;

</description>
      <category>porkbun</category>
      <category>ddns</category>
      <category>homelab</category>
      <category>dns</category>
    </item>
    <item>
      <title>Monitoring Docker Hub limits with Prometheus</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Sat, 26 Apr 2025 23:12:22 +0000</pubDate>
      <link>https://forem.com/jadolg/monitoring-docker-hub-limits-with-prometheus-1647</link>
      <guid>https://forem.com/jadolg/monitoring-docker-hub-limits-with-prometheus-1647</guid>
      <description>&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;Once upon a time, Docker decided to spoil us all developers and operators and had unlimited access to Docker Hub. You could pull as many images, as many times as your heart desired without any constraint, so we got used to it and never really thought about how good of a deal was that.&lt;/p&gt;

&lt;p&gt;Unfortunately, free and unlimited almost never go hand to hand and the day came when Docker decided to impose limits to how much you can pull from the mothership. At this point (April 2025) the limits have changed twice and the current numbers can be found at &lt;a href="https://docs.docker.com/docker-hub/usage/" rel="noopener noreferrer"&gt;https://docs.docker.com/docker-hub/usage/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Many of us just created users for those machines with heavy &lt;code&gt;docker pull&lt;/code&gt; usage and for most of it, it was fine. Until eventually something breaks. We started using caches where possible, but in some cases, the need for direct access was still there and therefore the need to know how much more can we pull for now and if any account had run dry.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prometheus + Alertmanager + Grafana
&lt;/h1&gt;

&lt;p&gt;If you've been operating services for the last decade, chances are you've meet these tools. &lt;br&gt;
For the uninitiated: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; is used to collect metrics of your systems over time.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://prometheus.io/docs/alerting/latest/alertmanager/" rel="noopener noreferrer"&gt;Alertmanager&lt;/a&gt; Takes care of handling alerts and distributing them to the right receiver.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; Is used to visualize metrics, logs, traces, and by the time you read this probably other things 😄&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These tools are widely used by operations teams as the backbone for monitoring and alerting and are the de facto standard in my opinion. This is not a tutorial about these tools specifically but I will explain how to pull the data into the tools and configure dashboards and alerts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Prometheus exporters
&lt;/h2&gt;

&lt;p&gt;Now, how do we get these metrics you ask? Well, we need to use a Prometheus exporter. This one specifically: &lt;a href="https://github.com/jadolg/dockerhub-pull-limit-exporter" rel="noopener noreferrer"&gt;https://github.com/jadolg/dockerhub-pull-limit-exporter&lt;/a&gt;.&lt;br&gt;
Prometheus itself doesn't directly integrate with every service. Exporters are services that get information from other software/services and expose in a &lt;a href="https://prometheus.io/docs/concepts/data_model/" rel="noopener noreferrer"&gt;format&lt;/a&gt; that Prometheus understands. Once active, you can add the exporter as a target to your Prometheus server and it will start scraping metrics from it periodically.&lt;/p&gt;
&lt;h1&gt;
  
  
  Installing
&lt;/h1&gt;

&lt;p&gt;The best and fastest way in my opinion to deploy the exporter is using docker compose. In your server (that already has docker and docker compose available), create a directory to place the configuration:&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;mkdir &lt;/span&gt;dockerhub-pull-limit-exporter
&lt;span class="nb"&gt;cd &lt;/span&gt;dockerhub-pull-limit-exporter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download the example configuration&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-O&lt;/span&gt; config.yaml https://raw.githubusercontent.com/jadolg/dockerhub-pull-limit-exporter/refs/heads/main/config.example.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And replace the example credentials with yours. You can add as many accounts as your heart desires.&lt;/p&gt;

&lt;p&gt;You can also adjust the interval in which the exporter gets its information from DockerHub and the timeout for these requests.&lt;br&gt;
Once the configuration is in place, We need a docker compose file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;dockerhub-pull-limit-exporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/jadolg/dockerhub-pull-limit-exporter&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9101:9101&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="s"&gt;./config.yaml:/config.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save it into the same directory and start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Prometheus
&lt;/h1&gt;

&lt;p&gt;And now it's time to setup Prometheus. There are many ways to do this since there are many ways to install Prometheus in the first place, but the general idea is the same. We need to add the exporter as a target to our Prometheus server so it knows what to scrape. Edit your Prometheus configuration and add the following target to your &lt;code&gt;scrape_configs&lt;/code&gt; replacing &lt;strong&gt;my-server&lt;/strong&gt; with your server's address:&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;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;                                                                                                         
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dockerhub_pull_limit_exporter'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&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;my-server:9101'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After restarting Prometheus, the new metrics should available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2w4zevgyqkr6uxpksmpv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2w4zevgyqkr6uxpksmpv.png" alt="Prometheus metrics" width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Grafana
&lt;/h1&gt;

&lt;p&gt;Now let's see what the metrics have to say. Head on to your Grafana and in the &lt;strong&gt;Dashboards&lt;/strong&gt; section click &lt;strong&gt;New/Import&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F521v1szx2m2o1b93lrzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F521v1szx2m2o1b93lrzt.png" alt="Grafana New" width="216" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Import view use ID &lt;strong&gt;23342&lt;/strong&gt; (created specifically for this exporter) and click Load and then Load (Weird choice of options, I know). Dashboard can also be found in &lt;a href="https://grafana.com/grafana/dashboards/23342-dockerhub-pull-limits/" rel="noopener noreferrer"&gt;Grafana's site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfj3h75igesnd1e7d3uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfj3h75igesnd1e7d3uy.png" alt="Grafana Import" width="793" height="825"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If everything goes right and your metrics are available you should be greeted by a shinny new dashboard like mine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fre00745djdvyc5j3pmg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fre00745djdvyc5j3pmg1.png" alt="Grafana Dashboard" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Alerts
&lt;/h1&gt;

&lt;p&gt;Now we are collecting the data and we are able to see it but we need to get alerted when our limits are running low. In order to do that we need to add a new rule file to our Prometheus configuration:&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;rule_files&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;alerts.dockerhub.rules.yml"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in that file we'll add the following alerts:&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;groups&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;DockerHubPullLimits&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DockerHubPullsRemainingLow&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dockerhub_pull_remaining_total/dockerhub_pull_limit_total * 100 &amp;lt; 10&lt;/span&gt; 
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Account&lt;/span&gt;&lt;span class="nv"&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;$labels.account&lt;/span&gt;&lt;span class="nv"&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;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;used&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;90%&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;its&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pull&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;limit"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DockerHubPullsRemainingLowCritical&lt;/span&gt;
        &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dockerhub_pull_remaining_total &amp;lt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5m&lt;/span&gt;
        &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;critical&lt;/span&gt;
        &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Account&lt;/span&gt;&lt;span class="nv"&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;$labels.account&lt;/span&gt;&lt;span class="nv"&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;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;used&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;100%&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;its&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;pull&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;limit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Prometheus and the alerts should be loaded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11hnixfww2d4m01m7acv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11hnixfww2d4m01m7acv.png" alt="Prometheus alerts" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These alerts will be triggered when the accounts have used &lt;strong&gt;90%&lt;/strong&gt; of the current limit and then when they have used &lt;strong&gt;100%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Next, &lt;a href="https://prometheus.io/docs/alerting/latest/configuration/" rel="noopener noreferrer"&gt;configure Alertmanager&lt;/a&gt; to route this alert to your favorite notification channel and alerts will get to you when they trigger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpu5sqy2rt73mztwbg0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffpu5sqy2rt73mztwbg0d.png" alt="Slack Alerts" width="672" height="299"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Final words
&lt;/h1&gt;

&lt;p&gt;Collecting metrics is an essential part of operating any software system. It's the window to your service's health and performance, and should be the main tool to use to diagnose when things go wrong. If there's a problem, there should be an alert for it and that alert should be based on metrics.&lt;/p&gt;

&lt;p&gt;With all this in place, the next time my services run out of docker pulls, I will know immediately, and if they are failing, I will have all the information I need to know if this is the cause. I will also have historical data which I'll be able to analyze to know what the trending usage is and be ready to improve my setup based on that data.&lt;/p&gt;

&lt;p&gt;And that's all for today folks! Happy monitoring!&lt;/p&gt;

&lt;p&gt;cover image: &lt;a href="https://www.freepik.com/free-vector/switches-buttons-control-panel-vector-illustrations-set-retro-control-console-terminal-elements-dials-knobs-dashboard-system-monitor-display-technology-equipment-concept_28480839.htm" rel="noopener noreferrer"&gt;https://www.freepik.com/free-vector/switches-buttons-control-panel-vector-illustrations-set-retro-control-console-terminal-elements-dials-knobs-dashboard-system-monitor-display-technology-equipment-concept_28480839.htm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>prometheus</category>
      <category>monitoring</category>
      <category>docker</category>
      <category>dockerhub</category>
    </item>
    <item>
      <title>Hunting down performance issues. A small DevOps story.</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Sat, 13 Apr 2024 20:33:15 +0000</pubDate>
      <link>https://forem.com/jadolg/hunting-down-performance-issues-a-small-devops-story-2hb</link>
      <guid>https://forem.com/jadolg/hunting-down-performance-issues-a-small-devops-story-2hb</guid>
      <description>&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;Here comes the story of another operation:&lt;/p&gt;

&lt;p&gt;One of my services (I both develop and operate it) frequently does "checks" on proxies through another service. This means a lot of service calls and some cryptography happening in the background so I was expecting it to use the CPU. The odd thing I was not expecting is that it did many many checks. For context, The check cycles were taking hours.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4dispowyeemhh4lrzmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs4dispowyeemhh4lrzmi.png" alt=" " width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This had me puzzled for some time and it was impacting the performance of the whole server, so the first step consisted in just limiting the amount of CPU that the container could use so it became a good neighbor. The performance hit was not critical, but it was unacceptable.&lt;/p&gt;

&lt;h1&gt;
  
  
  Patching the roof
&lt;/h1&gt;

&lt;p&gt;Since the service is running on docker-compose, the solution,  was rather simple:&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;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adding this to my service guarantees it won't hug all 4 cores in this server, but, as you can see in the following chart, the problem remained:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3l1y5fmkreutoo8qg88.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3l1y5fmkreutoo8qg88.png" alt=" " width="800" height="293"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Dig deeper!
&lt;/h1&gt;

&lt;p&gt;The amount of checks per second went down and all the other operations in the server were more responsive, but the time of the check bursts went very much up. We still have a problem to fix, we just made it good enough so the impact is not damaging the quality of the service.&lt;/p&gt;

&lt;p&gt;The next step would be to check the logs and surprise, my logs were not that good or easy to check, so I decided to start logging into the database the times for every task I executed. To my surprise, the task I thought was a long-running task because the previous chart, was only behaving like that because it got constantly re-scheduled. It was able to finish in about 15 minutes, but then I had many of these 15-minute tasks all over the logs.&lt;/p&gt;

&lt;h1&gt;
  
  
  The solution
&lt;/h1&gt;

&lt;p&gt;Turns out &lt;a href="https://docs.celeryq.dev/" rel="noopener noreferrer"&gt;Celery&lt;/a&gt; was scheduling the task over and over again. In the end, I could not manage to configure Celery so it would not do this, so I decided to give alternatives a try and found &lt;a href="https://huey.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;huey&lt;/a&gt; and it worked like a charm. Almost no changes and it works as a complete in-place replacement at least for my use case (running one task every 20 minutes and another every 6 hours). &lt;/p&gt;

&lt;p&gt;The performance improvement was so great I'm now able to schedule the task that previously was executed every 6 hours, every hour.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpggzabkpsdsul7x6mgzy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpggzabkpsdsul7x6mgzy.png" alt=" " width="800" height="290"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I still left the limit on CPU so if the service begins to misbehave again it does not affect its neighbors and I kept the new logs to have a more visible way to find problems.&lt;/p&gt;

&lt;h1&gt;
  
  
  Lessons learned
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Without metrics, I would not even notice the problem. The platform was working "just fine", but the numbers didn't add up.&lt;/li&gt;
&lt;li&gt;Question your metrics, even if they are on the baseline (it has always been like that). Your software might be misbehaving from the beginning and you will not notice the issue only by hunting irregularities.&lt;/li&gt;
&lt;li&gt;Your logs are your best friends. The logs will tell you what's happening in the system. Keep them coming, but keep them lean. Too many logs are sometimes worse than too few.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that's all folks. I hope you enjoy the read and I hope your services are up (and measured). Happy hacking!&lt;/p&gt;

</description>
      <category>metrics</category>
      <category>service</category>
      <category>web</category>
      <category>devops</category>
    </item>
    <item>
      <title>Tunneling all your internet connections through a socks proxy</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Thu, 26 Aug 2021 17:11:08 +0000</pubDate>
      <link>https://forem.com/jadolg/tunneling-all-your-internet-connections-through-a-socks-proxy-4f5i</link>
      <guid>https://forem.com/jadolg/tunneling-all-your-internet-connections-through-a-socks-proxy-4f5i</guid>
      <description>&lt;h1&gt;
  
  
  A story
&lt;/h1&gt;

&lt;h2&gt;
  
  
  The seed of the problem
&lt;/h2&gt;

&lt;p&gt;Not so many years ago when I used to live in Cuba, the only access to the internet I could use was using a Proxy set by the institution I was working for or the college I studied at. The managers on these networks used to restrict the contents you could access as hard as possible so at work you could not check Facebook or other social media, but that was not the main problem. Since Cuba is embargoed by the United States of America, several important resources like Docker images, Gitlab, and Google resources for coding in Android were all blocked for any Cuban IP. Conclusion: you need to use a VPN if you want anything done.&lt;br&gt;
Not being able to pay for a VPS (no such thing as credit cards in Cuba) I was heavily using Tor, YourFreedom, or even an SSH tunnel if someone borrowed me their access to a VPS, and many other alternatives to circumvent censorship. Most of the time, these connections ended up in being a socks proxy either on my computer or my local trusted network, and I could use them easily from my browser or on several applications using &lt;a href="https://linux.die.net/man/8/tsocks" rel="noopener noreferrer"&gt;tsocks&lt;/a&gt; or &lt;a href="https://github.com/haad/proxychains" rel="noopener noreferrer"&gt;proxychains&lt;/a&gt;. But the holy grail of this problem was to get all the traffic from your computer to be tunneled only using a socks proxy, and that I could not achieve. Until now :-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Recent development
&lt;/h2&gt;

&lt;p&gt;What I could not foresee is that after all these years, a friend of mine would also face the same problem. As for today, two great alternatives for this are a good match in my opinion. &lt;br&gt;
First, we have &lt;a href="https://github.com/sshuttle/sshuttle" rel="noopener noreferrer"&gt;sshuttle&lt;/a&gt; and I absolutely love it. It helped me a lot during some time but it has a downside. It only works on top of SSH which is currently blocked by the only Cuban telecommunications company so we could not really use it.&lt;br&gt;
Second, we have &lt;a href="https://getoutline.org/" rel="noopener noreferrer"&gt;Outline&lt;/a&gt; and this one is also brilliant. I have read a lot about it because it seems it's heavily used to bypass censorship and all it is is a wrapper around a very nice set of tools. It basically sets a &lt;a href="https://shadowsocks.org/en/index.html" rel="noopener noreferrer"&gt;shadowsocks&lt;/a&gt; connection and configures your device so it behaves like a VPN. This seemed to be the right way to go, but for some odd reason, it did not want to work properly on my friend's computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not just using a VPN?
&lt;/h2&gt;

&lt;p&gt;Well, Cuba is not precisely happy about their citizens using tricks to bypass their censorship and control, so they have managed to block protocols like OpenVPN. Every VPN that uses a handshake/header which is easily identifiable can be easily blocked. We could set up some obfuscation on top of the VPNs but this will require our users to also set up something on their side and that's "just fine" for a tech-savvy person, but not for our elders. In most cases, Outline is just perfectly fine because it's very hard to identify or block shadowsocks.&lt;/p&gt;

&lt;h1&gt;
  
  
  Making it work on a broken Linux
&lt;/h1&gt;

&lt;p&gt;Most people will just re-install their OS and try again, but this time that was not an option. Also, I really wanted to fight this problem again, so, since Outline was the fittest option, I decided to go on and see what they are doing under the hood. They are open source (&lt;a href="https://github.com/Jigsaw-Code/outline-client" rel="noopener noreferrer"&gt;https://github.com/Jigsaw-Code/outline-client&lt;/a&gt;) so this was kind of an easy task.&lt;br&gt;
Sadly, we could not isolate the problem in my friend's computer, but we were able to reproduce what the Outline folks do, and here's a guide to that:&lt;/p&gt;

&lt;h2&gt;
  
  
  Just tell me how to do it already!
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Start a shadowsocks server if you don't have one set already &lt;code&gt;ss-server -p 6276 -k password&lt;/code&gt;. The IP address of this server is going to be $server_ip from now on.&lt;/li&gt;
&lt;li&gt;Install the &lt;code&gt;shadowsocks-libev&lt;/code&gt; package. This will provide you with all the shadowsocks utilities and will set up your local proxy connected to the &lt;strong&gt;shadowsocks&lt;/strong&gt; server.&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;badvpn-tun2socks&lt;/code&gt;. This application will tunnel all your data using a &lt;strong&gt;Tun&lt;/strong&gt; network interface in Linux. To install it clone their repository &lt;a href="https://github.com/ambrop72/badvpn" rel="noopener noreferrer"&gt;https://github.com/ambrop72/badvpn&lt;/a&gt; and follow the compile and install instructions &lt;a href="https://github.com/ambrop72/badvpn/wiki/Tun2socks#installation" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Now, let's create a new &lt;strong&gt;tun&lt;/strong&gt; network interface &lt;code&gt;ip tuntap add dev tun0 mode tun user my_user&lt;/code&gt;, add an IP address to it &lt;code&gt;ip a add 10.0.0.1/24 dev tun0&lt;/code&gt;, and bring the interface up &lt;code&gt;ip link set dev tun0 up&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Connect to our shadowsocks server &lt;code&gt;ss-local -s $server_ip -p 6276 -k password -l 1080&lt;/code&gt; and  give it a try &lt;code&gt;curl --socks5 socks5://localhost:1080 https://myip.wtf/json&lt;/code&gt;. If it shows your server IP address it means it's working.&lt;/li&gt;
&lt;li&gt;Start &lt;strong&gt;badvpn-tun2socks&lt;/strong&gt; to start tunneling the data sent to &lt;code&gt;tun0&lt;/code&gt; to the socks proxy &lt;code&gt;badvpn-tun2socks --tundev tun0 --netif-ipaddr 10.0.0.2 --netif-netmask 255.255.255.0 --socks-server-addr 127.0.0.1:1080&lt;/code&gt;. After this point you should be able to ping the virtual gateway &lt;code&gt;ping 10.0.0.2&lt;/code&gt; with successful results.&lt;/li&gt;
&lt;li&gt;Add a network route to guarantee your socks proxy is still going to connect using your default gateway &lt;code&gt;ip r a $server_ip via $default_gateway&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a default route with a metric lower than the one provided by NetworkManager for your default gateway &lt;code&gt;ip r a default via 10.0.0.2 metric 10&lt;/code&gt;
And this is it. After this point, all the connections on your Linux machine are routed to the &lt;strong&gt;tun0&lt;/strong&gt; interface and therefore to the socks proxy. Do a quick &lt;code&gt;curl https://myip.wtf/json&lt;/code&gt; to check that you are in the location where your shadowsocks server is and enjoy another cup of coffee.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Final words
&lt;/h1&gt;

&lt;p&gt;No, you probably won't need any of this if Outline works for you, but for me, it was very satisfying to finally beat this problem and to help my friend. We could not find why Outline was not working for him, but we managed to reproduce almost exactly what Outline does and had fun in the process.&lt;br&gt;
Did you enjoy reading? Leave a &amp;lt;3 for me on this post and share it with your nerdy friends :-)&lt;/p&gt;

</description>
      <category>vpn</category>
      <category>linux</category>
      <category>proxy</category>
      <category>shadowsocks</category>
    </item>
    <item>
      <title>Avoid alert fatigue in your CI/CD</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Tue, 09 Feb 2021 12:06:23 +0000</pubDate>
      <link>https://forem.com/jadolg/avoid-alert-fatigue-in-your-ci-cd-5657</link>
      <guid>https://forem.com/jadolg/avoid-alert-fatigue-in-your-ci-cd-5657</guid>
      <description>&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;My team was having issues with not meaningful notifications for our builds getting in our way and we built a &lt;a href="https://github.com/marketplace/actions/should-i-notify-slack" rel="noopener noreferrer"&gt;GitHub action&lt;/a&gt; to fix it.&lt;/p&gt;

&lt;h1&gt;
  
  
  A story
&lt;/h1&gt;

&lt;p&gt;All those essential meetings are over. You finally have the time to focus and do some coding. One of your teammates volunteers for working in pairs and together start building that new feature everyone is so eager to get. Ten minutes into analyzing the task a chime sounds and a Slack notification shows on your screen. It is the alerts channel for your project's builds. Other teammates have just pushed code for their task and it has built correctly. Their code is now deployed and everything seems fine. You close the notification and continue with your task, but the rest of the team is also moving and before you notice, you have checked the notifications channel five times in the last hour only to see green builds.&lt;/p&gt;

&lt;p&gt;You think, let's just turn on notifications only for the broken builds so I don't waste my time checking things that are not broken, but after some days with this approach, you notice two problems: You don't find out if the status of the builds is green again if you don't manually check and now it feels sad to check the channel and see only red statuses of broken builds. You also want to know when there has being a recovery. The problem: There's no simple way of doing it with GitHub actions which is what the team is using for CI/CD.&lt;/p&gt;

&lt;p&gt;Do this story rings a bell for you?&lt;/p&gt;

&lt;h1&gt;
  
  
  A solution
&lt;/h1&gt;

&lt;p&gt;Lucky for us, GitHub Actions is a very flexible platform and the GitHub API helps a lot in finding information about your projects and builds. At this point, we decided to make our own action to tell us if we should notify Slack about the current build status.&lt;/p&gt;

&lt;p&gt;This is the strategy we wanted to apply:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the current build failed, then it will recommend sending the message&lt;/li&gt;
&lt;li&gt;If the current build succeeded, and the previous one failed, then it will recommend sending the message&lt;/li&gt;
&lt;li&gt;If the current build succeeded, and the previous one succeeded, then it will recommend NOT sending the message&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With this stated, we can move onto the next step which is finding the right GitHub tools to build our action. Turns out that actions don't provide previous builds information out of the box, but we can always query the GitHub API and so we did. Turns out there's an &lt;a href="https://docs.github.com/en/rest/reference/actions#list-workflow-runs" rel="noopener noreferrer"&gt;API endpoint&lt;/a&gt; that will tell you the status of the runs for a specific workflow. For this, we need the current workflow id (which for some reason is not injected in the context by GitHub) but we can also get it from the information we get when querying for the current &lt;code&gt;workflow run&lt;/code&gt; using this &lt;a href="https://docs.github.com/en/rest/reference/actions#get-a-workflow-run" rel="noopener noreferrer"&gt;API endpoint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the previous build information, we now only need to check if any of the jobs for the build that are required have failed. For this, we will use in our job the &lt;a href="https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idneeds" rel="noopener noreferrer"&gt;needs&lt;/a&gt; property and the strategy here is passing it to our action and searching for the word &lt;strong&gt;failed&lt;/strong&gt; there. If any of the jobs needed failed then we need to send a notification.&lt;/p&gt;

&lt;h1&gt;
  
  
  Show me how to use it already!
&lt;/h1&gt;

&lt;p&gt;Here is what we ended up writing &lt;a href="https://github.com/marketplace/actions/should-i-notify-slack" rel="noopener noreferrer"&gt;https://github.com/marketplace/actions/should-i-notify-slack&lt;/a&gt; and you can use it too in your actions since we decided to publish it and make it open source.&lt;/p&gt;

&lt;p&gt;In the following example, we have two jobs in our workflow and we need to send notifications according to the previously discussed conditions. To accomplish this we &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add a third job that sends the notification (&lt;code&gt;slack-workflow-status&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add a condition to run this job even when the other jobs fail (&lt;code&gt;if: always()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;job1&lt;/code&gt; and &lt;code&gt;job2&lt;/code&gt; to the &lt;code&gt;needs&lt;/code&gt; of the &lt;code&gt;slack-workflow-status&lt;/code&gt; job&lt;/li&gt;
&lt;li&gt;Use our action to determine if we send the update to slack adding the condition &lt;code&gt;if: steps.should_notify.outputs.should_send_message == 'yes'&lt;/code&gt; to the actual notification step which in our case uses the &lt;code&gt;Gamesight/slack-workflow-status&lt;/code&gt; action. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please notice that the example is using only the &lt;code&gt;main&lt;/code&gt; branch:&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;job1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run the script&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;./script1.sh&lt;/span&gt;
&lt;span class="na"&gt;job2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run the script&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;./script2.sh&lt;/span&gt;

&lt;span class="na"&gt;slack-workflow-status&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;Post workflow status To Slack&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="s"&gt;job1&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;job2&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always() &amp;amp;&amp;amp; github.ref == 'refs/heads/main'&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;Determine if we need to notify&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;Jimdo/should-i-notify-action@main&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;should_notify&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;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
        &lt;span class="na"&gt;needs_context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ toJson(needs) }}&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;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;Slack workflow notification&lt;/span&gt;
      &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.should_notify.outputs.should_send_message == 'yes'&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;Gamesight/slack-workflow-status@master&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;repo_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.GITHUB_TOKEN}}&lt;/span&gt;
        &lt;span class="na"&gt;slack_webhook_url&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://hooks.slack.com/services/...'&lt;/span&gt;
        &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;notifications-slack-channel'&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&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;great&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bot'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Final words
&lt;/h1&gt;

&lt;p&gt;It might seem like having meaningless notifications is not such a big problem but it actually is. Whenever this happens, you and your team will start ignoring them and will notice the problems later than expected. Cutting the notifications/alerts to only what is important is crucial for effectively handling problems when they occur and to protect your focus time from distractions.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;Cover image from &lt;a href="https://www.recordedfuture.com/security-operations-alert-fatigue/" rel="noopener noreferrer"&gt;https://www.recordedfuture.com/security-operations-alert-fatigue/&lt;/a&gt;&lt;sup&gt;&lt;/sup&gt;&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>notifications</category>
      <category>slack</category>
      <category>teamwork</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Improving my home-office environment with noise detection</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Tue, 02 Feb 2021 20:31:32 +0000</pubDate>
      <link>https://forem.com/jadolg/improving-my-home-office-environment-with-noise-detection-421e</link>
      <guid>https://forem.com/jadolg/improving-my-home-office-environment-with-noise-detection-421e</guid>
      <description>&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;I used a &lt;a href="https://microbit.org/" rel="noopener noreferrer"&gt;BBC micro:bit v2&lt;/a&gt; to notify me when I am speaking too loud. Here is the project: &lt;a href="https://github.com/jadolg/sound-alert-microbit" rel="noopener noreferrer"&gt;https://github.com/jadolg/sound-alert-microbit&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Why?
&lt;/h1&gt;

&lt;p&gt;I usually don't speak too loud. Even when I'm mad, I try to remain calm and not to increase the volume of my voice as I dislike loud folks myself. There are, anyway, moments in which I involuntarily do speak really loud: When I'm wearing headphones and when I get really excited about what I'm talking about. And, guess what: That's exactly my everyday scenario since COVID19 forced us to close the offices and work from home. I work remotely for a fantastic software company and yes, my work is really exciting. On top of that, we do a lot of pair programming and have tens of meetings every week to discuss what we want to do and how we want to do it. All of this while wearing headphones that isolate me pretty well from the environment, so as you can imagine, I started speaking really loud.&lt;br&gt;
This whole situation was not really fun especially for my wife. She is also a software engineer and we share the same space for working.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ziv7t15lnmco2ph4rhb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1ziv7t15lnmco2ph4rhb.jpg" alt="our office" width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
Just imagine having meetings at the same time 😬&lt;/p&gt;
&lt;h1&gt;
  
  
  What to do
&lt;/h1&gt;

&lt;p&gt;I needed to do something regarding this issue and engineers will try to solve everything with tech right? I researched for some hours and found out that the &lt;a href="https://microbit.org/" rel="noopener noreferrer"&gt;BBC micro:bit&lt;/a&gt; had released a new version of their development board. What was specifically so nice about this board is that it comes already packed with a bunch of sensors including a microphone. This also comes with a very nice price tag and a system so simple to use even kids can do it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using MakeCode
&lt;/h2&gt;

&lt;p&gt;You don't know how to code? No problem, you don't need to write a single line of code. &lt;a href="https://makecode.microbit.org/" rel="noopener noreferrer"&gt;Microsoft MakeCode&lt;/a&gt; will allow you to express your automation desires in an intuitive way. After some minutes, this is what I ended up with:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fztork5rt3gi3hzwjzwbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fztork5rt3gi3hzwjzwbm.png" alt="code blocks" width="800" height="484"&gt;&lt;/a&gt;&lt;br&gt;
It is also very useful having an emulator and being able to play around and try everything.&lt;br&gt;
MakeCode also allows you to code your solution in Python and JavaScript and applies translation from one codebase to another, so what I have just shown you in blocks looks like this in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;sound_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="n"&gt;sound_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_button_pressed_a&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;sound_enabled&lt;/span&gt;
    &lt;span class="n"&gt;sound_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sound_enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_button_pressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_button_pressed_a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_forever&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;global&lt;/span&gt; &lt;span class="n"&gt;sound_level&lt;/span&gt;
    &lt;span class="n"&gt;sound_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sound_level&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;sound_level&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&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;sound_enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;music&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play_tone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;262&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;music&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BeatFraction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WHOLE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;show_icon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IconNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EIGTH_NOTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear_screen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;on_forever&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and like this in JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sound_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sound_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onButtonPressed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;on_button_pressed_a&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sound_enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sound_enabled&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forever&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;on_forever&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sound_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;soundLevel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sound_level&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sound_enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;music&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;playTone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;262&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;music&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;beat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;BeatFraction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Whole&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;IconNames&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EigthNote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;basic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As much as I want to explain much more, I would be only grasping the surface of this great tool, so go and give it a try 😄&lt;/p&gt;

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

&lt;p&gt;I ended up with a pretty cool gadget that warns me visually if I'm being too loud and with the press of a button I can set it so it also beeps at me. I'm trying to condition myself to be a better home-office mate for my wife. At this point, I think I have not made much progress at it 😅 but this is only the beginning.&lt;br&gt;
You can find the code for the project in &lt;a href="https://github.com/jadolg/sound-alert-microbit" rel="noopener noreferrer"&gt;this&lt;/a&gt; repository (which MakeCode allows you to do) and explore and play with it.&lt;/p&gt;

&lt;p&gt;&lt;sup&gt;Kudos to &lt;a href="https://twitter.com/ClemOno2" rel="noopener noreferrer"&gt;Clem Onojeghuo&lt;/a&gt; for the cover picture&lt;sup&gt;&lt;/sup&gt;&lt;/sup&gt;&lt;/p&gt;

</description>
      <category>homeoffice</category>
      <category>microbit</category>
      <category>python</category>
      <category>health</category>
    </item>
    <item>
      <title>Decode JSON Web Token in your terminal</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Sat, 31 Oct 2020 19:16:16 +0000</pubDate>
      <link>https://forem.com/jadolg/decode-json-web-token-in-your-terminal-3pl9</link>
      <guid>https://forem.com/jadolg/decode-json-web-token-in-your-terminal-3pl9</guid>
      <description>&lt;h1&gt;
  
  
  Inspiration
&lt;/h1&gt;

&lt;p&gt;Probably there are tens of applications to do this. I know for sure they are out there, but at the time I was reading this post on Reddit about how to get the downloads limit on DockerHub I could not find any of them. The requirements were pretty straight forward. I need one application I can push a token and it will output formatted &lt;strong&gt;JSON&lt;/strong&gt;. No validation or fancy things required. I would love to colorize the output, but that's completely optional.&lt;/p&gt;

&lt;p&gt;And out of this need, I got my Saturday project on.&lt;/p&gt;

&lt;p&gt;We don't need to re-invent the wheel. Decoding is already done by a lot of libraries, I just needed to pick one and wrap everything in a nice GO CLI interface. &lt;/p&gt;

&lt;h2&gt;
  
  
  Development
&lt;/h2&gt;

&lt;p&gt;One of the main reasons why I love to use GO is because I can build my applications for almost any platform/architecture. The binaries are a bit "fat", but let's face it: Storage is already pretty cheap and most people will just not care about a couple of megabytes.&lt;br&gt;
I ended up searching for a nice library to do all the work for me and found &lt;strong&gt;go-jose&lt;/strong&gt;. This decodes my token without problems and places all the &lt;strong&gt;claims&lt;/strong&gt; into a GO map structure which I can later marshal as &lt;strong&gt;JSON&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;claims&lt;/span&gt; &lt;span class="k"&gt;map&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;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParseSigned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tokenString&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UnsafeClaimsWithoutVerification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;claims&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, all the hard work is done. Let's grab a beer and enjoy :-)&lt;br&gt;
I added an argument &lt;code&gt;-t&lt;/code&gt; to pass the token to my application:&lt;br&gt;
&lt;code&gt;tokenArgument := flag.String("t", "", "Token to decode. If not specified will try to read it from stdin.")&lt;/code&gt;&lt;br&gt;
And some code to read from &lt;code&gt;stdin&lt;/code&gt; if the token was not specified using the command line argument.&lt;br&gt;
Then added optional colorization because life is better with colors, so I used &lt;a href="https://github.com/TylerBrock/colorjson" rel="noopener noreferrer"&gt;TylerBrock/colorjson&lt;/a&gt; to optionally output colorized text.&lt;/p&gt;

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

&lt;p&gt;I was a bit lazy here, so I just added some tests to check that the main methods are still doing what they are supposed to do, added a GitHub action to build and test the application every time someone pushes it or creates a pull request, and that is it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Releasing
&lt;/h2&gt;

&lt;p&gt;There are tons of ways to release applications. So many package managers and intricate ways maintainers have decided they want their software to be installed. For now, I'll just release the binaries and the users just need to download them and use them as they want. There's also a GitHub action for releasing GO applications (&lt;a href="https://github.com/wangyoucao577/go-release-action" rel="noopener noreferrer"&gt;https://github.com/wangyoucao577/go-release-action&lt;/a&gt;) that worked perfectly for me. In the future, I'll be adding at least &lt;a href="https://en.wikipedia.org/wiki/Snap_(package_manager)" rel="noopener noreferrer"&gt;Ubuntu snaps&lt;/a&gt; support.&lt;/p&gt;

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

&lt;p&gt;As my requirements were pretty simple the usage of the application is also simple. Just pass the token as an argument &lt;code&gt;jwt-decode -t "ABeautifulToken"&lt;/code&gt; or pipe it in &lt;code&gt;echo "ABeautifulToken" | jwt-decode&lt;/code&gt; and it will do the work. Want colors? Use it with &lt;code&gt;-c&lt;/code&gt; and it will be 150% better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flg7755bk4f8eztbd61vb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flg7755bk4f8eztbd61vb.png" alt="Example" width="800" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;And that's it. &lt;a href="https://github.com/jadolg/jwt-decode" rel="noopener noreferrer"&gt;This&lt;/a&gt; is the result of my today's adventure trying to make everything nicer for me and my fellow devs. Feel free to leave some love materialized as GitHub stars for me.&lt;br&gt;
You can download the proper binary for your distribution and start decoding JSON Web Tokens like a jedi from your terminal today from the GitHub release page.&lt;/p&gt;

</description>
      <category>jwt</category>
      <category>terminal</category>
      <category>json</category>
    </item>
    <item>
      <title>A GitHub action to push messages to Rocket.Chat using credentials instead of webhooks</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Fri, 14 Aug 2020 21:46:41 +0000</pubDate>
      <link>https://forem.com/jadolg/a-github-action-to-push-messages-to-rocket-chat-using-credentials-instead-of-webhooks-go5</link>
      <guid>https://forem.com/jadolg/a-github-action-to-push-messages-to-rocket-chat-using-credentials-instead-of-webhooks-go5</guid>
      <description>&lt;h3&gt;
  
  
  My Workflow
&lt;/h3&gt;

&lt;p&gt;So I found out about &lt;a href="https://dev.to/t/actionshackathon"&gt;#ActionsHackathon&lt;/a&gt; thanks to a coworker ;-) and I like hackathons and stickers. I have been playing with GitHub actions during the last weeks and I was intrigued about how all of it works. I have worked with Travis, Bamboo, Gitlab-ci. It does not feel like any of them. That's why I decided to go with building an action myself.&lt;/p&gt;

&lt;p&gt;I selected a problem I like and that I have talked about before &lt;a href="https://dev.to/jadolg/rocketchat-notifications-on-your-cicd-pipeline-59p5"&gt;here&lt;/a&gt; and started playing around. I love &lt;a href="https://rocket.chat/" rel="noopener noreferrer"&gt;Rocket.Chat&lt;/a&gt; and I love chat notifications and the fact that you can notify a lot of people with a single message if your build breaks or if a new version of your application was deployed for testing is very convenient. &lt;a href="https://rocket.chat/" rel="noopener noreferrer"&gt;Rocket.Chat&lt;/a&gt; allows you to add integrations and push messages using webhooks and they actually have &lt;a href="https://github.com/marketplace/actions/rocket-chat-notification" rel="noopener noreferrer"&gt;a very nice GitHub action&lt;/a&gt; to do so. But what if you can't create integrations on this server because you don't have enough privileges? I wanted to be able to do it with only credentials, the URL for the &lt;a href="https://rocket.chat/" rel="noopener noreferrer"&gt;Rocket.Chat&lt;/a&gt; server and the name of the channel where I want to post messages.&lt;/p&gt;

&lt;p&gt;First, I'm not comfortable with using &lt;code&gt;javascript&lt;/code&gt; or &lt;code&gt;typescript&lt;/code&gt; and most of the actions I found were written using either of them. I was very happy when I discovered you can just use &lt;code&gt;Docker&lt;/code&gt; &lt;a href="https://docs.github.com/en/actions/creating-actions/dockerfile-support-for-github-actions" rel="noopener noreferrer"&gt;https://docs.github.com/en/actions/creating-actions/dockerfile-support-for-github-actions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I started a new &lt;a href="https://github.com/jadolg/rocketchat-notification-action" rel="noopener noreferrer"&gt;repository&lt;/a&gt; and begun writing with the code on the tutorial. Since I created a private repository I could only test the action on itself, so I started a disposable branch on GitHub, created a pull request to use my action, and started playing with different configurations from the same GitHub interface. I would commit the changes to the action on the &lt;code&gt;master&lt;/code&gt; branch and then test from the disposable branch.&lt;/p&gt;

&lt;p&gt;After a couple of hours, I had written and tested my new action and published it to the marketplace &lt;a href="https://github.com/marketplace/actions/rocket-chat-notification-with-credentials" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was fun working on it and the documentation was easy to follow. Now it's time to start actually using it on a project, showing it around and improving it :-)&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;DIY Deployments&lt;/p&gt;

&lt;h3&gt;
  
  
  Yaml File or Link to Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/jadolg/rocketchat-notification-action" rel="noopener noreferrer"&gt;https://github.com/jadolg/rocketchat-notification-action&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;Open source projects involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aleph-engineering/rocketchat-notification" rel="noopener noreferrer"&gt;https://github.com/aleph-engineering/rocketchat-notification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rocket.chat/" rel="noopener noreferrer"&gt;https://rocket.chat/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>actionshackathon</category>
      <category>github</category>
      <category>opensource</category>
    </item>
    <item>
      <title>TextMadeWeb: A website to browse the web while using a terrible internet connection</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Tue, 17 Mar 2020 16:37:32 +0000</pubDate>
      <link>https://forem.com/jadolg/textmadeweb-a-website-to-browse-the-web-while-using-a-terrible-internet-connection-ki6</link>
      <guid>https://forem.com/jadolg/textmadeweb-a-website-to-browse-the-web-while-using-a-terrible-internet-connection-ki6</guid>
      <description>&lt;h2&gt;
  
  
  A small introduction
&lt;/h2&gt;

&lt;p&gt;You are probably tired of me saying how bad the internet connection is overall in Cuba, so I'll skip a bit that part today :D&lt;br&gt;
A few years ago I was working with a terrible internet connection. I could not have better and loading pages on StackOverflow took minutes. Can you imagine? I was so frustrated and accomplishing so little at the time that I decided to fix the problem the way I know how to: programming a solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here goes the testing
&lt;/h2&gt;

&lt;p&gt;When I was in college I used to use &lt;strong&gt;iptables&lt;/strong&gt; to control my internet traffic. That way I could decide what I wanted to consume and reduce my internet consumption to save the scarce amount of megabytes (100) I was getting every month. Yes, I'm NOT talking about speed. That's the actual amount of megabytes I could use every month.&lt;br&gt;
So my first idea was to reuse my old &lt;strong&gt;iptables&lt;/strong&gt; script with lots and lots of blocked addresses. But this eventually became problematic. I needed to again start to update my lists with new addresses and deprecate some that didn't even exist anymore. It was also not portable, so I could not give it to my friends using Windows. I scratched that one really fast from the list.&lt;br&gt;
My second thought was to make a proxy to do the whole thing for me. I could distribute it or share it with my friends without problems but again updating it would be problematic so I again decided not to go that way.&lt;br&gt;
At the time, a lot of my friends were also using these web proxies to bypass blocked sites so I had the idea of making something like that but to strip the webs of everything but text (and some styling because reading code in plaintext is a natural cause for headaches).&lt;/p&gt;

&lt;h2&gt;
  
  
  The product
&lt;/h2&gt;

&lt;p&gt;I'm a lazy person :-) I know I should not be proud of it, but somehow I am. Being lazy makes you work extra to find a way to do things with less effort. In my case, I just realized that If I used a library to turn HTML into Markdown and then back to HTML, the result was tremendously clean. I just needed to then remove the images and trick the links so they will open inside the same site. I added a bit of compression and that's it.&lt;/p&gt;

&lt;p&gt;Here is the result:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa490v166aeo96wngwwjk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa490v166aeo96wngwwjk.png" alt="Front Page" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No styles, no images, no Javascript. Just plain text. You can find it hosted on &lt;a href="https://txtmdweb.herokuapp.com/" rel="noopener noreferrer"&gt;https://txtmdweb.herokuapp.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You introduce the address that you want to "textify" and hit the "Text it!" button. The result will be something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fad1dhj1m5skxqsku1b7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fad1dhj1m5skxqsku1b7d.png" alt="Texted page" width="800" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also do Google searches with it, but it is not particularly good at it :')&lt;/p&gt;

&lt;p&gt;I used &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; (1.11 at the time) to write the project and the sources can be found &lt;a href="https://github.com/jadolg/TextMadeWeb" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Disadvantages
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I only implemented &lt;strong&gt;getting&lt;/strong&gt; sites. No further interactions are permitted, so no login or cookies or anything but clear text which is publically available.&lt;/li&gt;
&lt;li&gt;No Javascript. Sadly, a lot of websites today are mostly Javascript. If the website you are trying to convert to text is using Javascript to load its content, this is not going to work.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I hope you liked the story and if you are in need of using such a tool (I really hope you are not), well now you know it exists. &lt;br&gt;
Want to make it better? PRs are open ;-) or just go open an issue.&lt;br&gt;
And that's all. If you liked the project, go hit that star button on GitHub which makes open-source developers like me very happy.&lt;/p&gt;

</description>
      <category>internet</category>
      <category>cuba</category>
    </item>
    <item>
      <title>Monitoring my internet connection with Micropython and ESP8266</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Mon, 23 Dec 2019 12:49:14 +0000</pubDate>
      <link>https://forem.com/jadolg/monitoring-my-internet-connection-with-micropython-and-esp8266-42lp</link>
      <guid>https://forem.com/jadolg/monitoring-my-internet-connection-with-micropython-and-esp8266-42lp</guid>
      <description>&lt;h2&gt;
  
  
  A small introduction
&lt;/h2&gt;

&lt;p&gt;I love playing with IoT devices. Coming from Cuba that's not a corresponded love. It is really difficult to get your hands on them as they are usually not sold on any market. Some projects like &lt;a href="https://twitter.com/thecubantech" rel="noopener noreferrer"&gt;The Cuban Tech Group&lt;/a&gt; have been doing an amazing job bringing tech and knowledge for all the interested parties for free but there is still a huge lack of development in that matter in the island. Thanks to some friends I managed to acquire some &lt;a href="https://en.wikipedia.org/wiki/ESP8266" rel="noopener noreferrer"&gt;ESP8266&lt;/a&gt; modules and I have played with them a lot. &lt;a href="https://micropython.org/" rel="noopener noreferrer"&gt;Micropython&lt;/a&gt; is an awesome tool to work with them and since I already knew Python and did not want to dig into learning how to code with all of the Arduino stack I limited myself to learn how to do things with micopython on these fantastic boards.&lt;/p&gt;

&lt;h2&gt;
  
  
  And the story actually starts
&lt;/h2&gt;

&lt;p&gt;Some months ago I visited my parents in the countryside Cuba. I was very happy because they have managed to do a contract with the telecommunications company to have an ADSL connection at home. ADSL is now coming to Cuba and the service is pretty new. Users not only have to pay for the connection on a monthly bases but they also need to login on a &lt;a href="https://en.wikipedia.org/wiki/Captive_portal" rel="noopener noreferrer"&gt;captive portal&lt;/a&gt; that works per time (yes, this telecommunications company is evil and greedy and it's the only telecommunications company in the country). Since you need to pay per time and every hour of internet costs 0.5 USD, my parents can't afford to have it connected all the time, so they only use internet during some small periods of time during the day. And then there was me wanting to be notified when I could use the wifi to save my data internet which is also very expensive in Cuba.&lt;/p&gt;

&lt;p&gt;I wanted to have some sort of indicator that will tell me when the ADSL router was actually connected to internet so I could use it and it needed to work even with me connected to another access point as I would probably be using internet from my phone almost all the time, and so, the idea came to my mind: Let's make a notification light with one ESP8266 board!&lt;/p&gt;

&lt;h2&gt;
  
  
  How should I check?
&lt;/h2&gt;

&lt;p&gt;Well, there are lots of recipes to check if you are connected to internet, but here goes my preferred one: I once found this Google URL &lt;a href="http://clients3.google.com/generate_204" rel="noopener noreferrer"&gt;http://clients3.google.com/generate_204&lt;/a&gt; that serves a simple purpose. Whenever you access it, the &lt;a href="https://www.restapitutorial.com/httpstatuscodes.html" rel="noopener noreferrer"&gt;HTTP response code&lt;/a&gt; will be &lt;strong&gt;204&lt;/strong&gt;. So I'll just periodically check this URL expecting a 204 status code. If I get a 200 (OK) it means the device is connected but I'm hitting the captive portal and if I have any other response code or any connection error, well it means the problem is somewhere else. Probably the device is disconnected or there are some communications problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Here comes Micropython
&lt;/h2&gt;

&lt;p&gt;First of all, I needed to flash micropython into my ESP8266 device (in my case a WemOS D1 mini) and you can find all the instructions on how to do it and how to start using the device &lt;a href="https://docs.micropython.org/en/latest/esp8266/tutorial/intro.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Be patient. It's easy once you understand the process ;-)&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's code
&lt;/h2&gt;

&lt;p&gt;In the end, I ended up with this small script that will manage the on-board led according to the internet status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;gc&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urequests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pin&lt;/span&gt;

&lt;span class="n"&gt;pin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;wlan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WLAN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;network&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STA_IF&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;wlan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;active&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;wlan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urequests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://clients3.google.com/generate_204&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;online&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;portal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;offline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;gc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will check every 10 seconds for a connection and let us know. I actually created a Github repository for it &lt;a href="https://github.com/jadolg/IoTInternetMonitior" rel="noopener noreferrer"&gt;https://github.com/jadolg/IoTInternetMonitior&lt;/a&gt; so please feel free to comment, improve and use :)&lt;/p&gt;

&lt;p&gt;Expected behavior:&lt;br&gt;
When the router is connected to internet the status led will be ON. When the captive portal is detected the status led will blink and when there's an error in the connection and there's no internet access the led will be completely OFF.&lt;/p&gt;

&lt;p&gt;Some things you probably noticed:&lt;br&gt;
Yes, there's a lot of printing in there. Feel free to remove it as it's not really needed if you are not "old-style debugging".&lt;br&gt;
Yes, for some reason the pin works completely in reverse so &lt;code&gt;pin.on()&lt;/code&gt; is actually turning the led off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;I had a lot of fun building the indicator and in the end, even my parents ended up using it to figure out if they had correctly logged out of the internet service. I hope that someday we will not need this kind of device for anything in Cuba and that being connected all the time to internet will be cheap and easy. Since it is sort of a hacky story about solving a problem with tech I decided to share it with you. I hope you enjoyed reading.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Image credits to &lt;a href="https://www.etsy.com/" rel="noopener noreferrer"&gt;https://www.etsy.com/&lt;/a&gt; . &lt;a href="https://www.etsy.com/il-en/listing/537115947/offline" rel="noopener noreferrer"&gt;https://www.etsy.com/il-en/listing/537115947/offline&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>micropython</category>
      <category>esp8266</category>
    </item>
    <item>
      <title>RocketChat notifications on your CI/CD pipeline</title>
      <dc:creator>Jorge Alberto Díaz Orozco (Akiel)</dc:creator>
      <pubDate>Fri, 12 Oct 2018 16:47:48 +0000</pubDate>
      <link>https://forem.com/jadolg/rocketchat-notifications-on-your-cicd-pipeline-59p5</link>
      <guid>https://forem.com/jadolg/rocketchat-notifications-on-your-cicd-pipeline-59p5</guid>
      <description>&lt;p&gt;Hey there :)&lt;br&gt;
I'm a big fan of &lt;a href="https://rocket.chat/"&gt;Rocket.Chat&lt;/a&gt;. Such a big fan that I even wrote &lt;a href="https://github.com/jadolg/rocketchat_API"&gt;my own REST API wrapper&lt;/a&gt; to use it with Python and convinced everyone in my company to migrate from Slack. And since I work in a software house we soon started using it for many things. We even have a bot to set up reminders in the organization :)&lt;br&gt;
One thing we love about the work we do is continuous integration. We are a very quality focused team and we like everything to be automated. I want to do &lt;code&gt;git push&lt;/code&gt; and everything should automagically start building, testing, measuring and deploying and I, of course, want to have notifications on my awesome chat application so my team is also aware of what's going on without even opening our CI tools.&lt;br&gt;
I know there are plenty of CI tools, but one of the more popular in my team is gitlab-ci. I personally switched from jenkins because I loved the way gitlab works all with Docker out of the box and I find the sintax more clear and beautiful, therefore I will write my examples using gitlab-ci notation.&lt;br&gt;
So, you can technically push notifications to RocketChat using only curl, but it will probably be verbose. First, you would have to login into your chat server and save the tokens and then post the message you want, all using, for instance, &lt;em&gt;curl&lt;/em&gt;.&lt;br&gt;
According to the REST API documentation, you would log in like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:3000/api/v1/login &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"username=myusername&amp;amp;password=mypassword"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;which will result in a response like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&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;"authToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9HqLlyZOugoStsXCUfD_0YdwnNnunAJF8V47U3QHXSq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"userId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aobEdbYhXfu5hkeqG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"me"&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;"_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;"aYjNnig8BEAWeQzMh"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rocket Cat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"emails"&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;"address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rocket.cat@rocket.chat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"verified"&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="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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"offline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusConnection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"offline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rocket.cat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"utcOffset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"roles"&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="s2"&gt;"admin"&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;"settings"&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;"preferences"&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;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;and you would need to save the values of &lt;em&gt;authToken&lt;/em&gt; and &lt;em&gt;userId&lt;/em&gt; to be used to post the message.&lt;br&gt;
Posting the message after this using &lt;em&gt;curl&lt;/em&gt; is also not difficult:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Auth-Token: 9HqLlyZOugoStsXCUfD_0YdwnNnunAJF8V47U3QHXSq"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-User-Id: aobEdbYhXfu5hkeqG"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-type:application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     http://localhost:3000/api/v1/chat.postMessage &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "channel": "#general", "text": "This is a test!" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It should be very easy to make a script with this, but since I wanted to practice some &lt;em&gt;GO&lt;/em&gt; I decided to make &lt;a href="https://github.com/aleph-engineering/rocketchat-notification"&gt;this application&lt;/a&gt; and since I love the way &lt;em&gt;GO&lt;/em&gt; just works almost everywhere it seemed like a good idea and it was.&lt;br&gt;
So the idea now is a little bit more simple:&lt;br&gt;
You just have to download the released application (or compile it yourself) and executed as shown in the help.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://github.com/aleph-engineering/rocketchat-notification/releases/download/1.4.1/rocketchat-notification &lt;span class="nt"&gt;-P&lt;/span&gt; /usr/bin/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /usr/bin/rocketchat-notification
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rocketchat-notification -c general -u user -p ${ROCKET_PASSWORD} -s https://myserver.rocket.chat -m "hello from terminal"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pretty simple right?&lt;br&gt;
Now let's use it in a sentence :)&lt;br&gt;
On my gitlab-ci (.gitlab-ci.yml on your files) I would do something like this:&lt;br&gt;
First, let's put the password of the user we want to use as an environment variable on gitlab so it is not persisted on our files improving our security:&lt;br&gt;
This is done on our project's settings under CI/CD &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vHKp8kQ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kb6mg5y3i3sgzmhilyoc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vHKp8kQ5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/kb6mg5y3i3sgzmhilyoc.png" alt="gitlab_CI"&gt;&lt;/a&gt;&lt;br&gt;
And let us write in our &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; something 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;stages&lt;/span&gt;&lt;span class="pi"&gt;:&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:stable-dind&lt;/span&gt;

&lt;span class="na"&gt;test_nitofication&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&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;before_script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wget https://github.com/aleph-engineering/rocketchat-notification/releases/download/1.4.1/rocketchat-notification -P /usr/bin/ &amp;amp;&amp;amp; chmod +x /usr/bin/rocketchat-notification&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;rocketchat-notification -c general -u gitlab -p ${ROCKET_PASSWORD} -s https://yourserver.rocket.chat -m "hello from CI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Commit and push your changes and wait for the notification :)&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zFwP1gTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5puovnncfjl7k44wnfhy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zFwP1gTe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5puovnncfjl7k44wnfhy.png" alt="notification"&gt;&lt;/a&gt;&lt;br&gt;
Do you want to send the output of a command to your chat? No problem. You can always run rocketchat-notification to read from standard input like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker ps | rocketchat-notification -c general -u gitlab -p ${ROCKET_PASSWORD} -s https://yourserver.rocket.chat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and using the &lt;code&gt;-code true&lt;/code&gt; flag your text will appear in a codebox in the chat.&lt;br&gt;
And that's it. I hope you find this interesting and practical and have fun with your Continuous Integration.&lt;/p&gt;

</description>
      <category>rocketchat</category>
      <category>cicd</category>
      <category>go</category>
      <category>gitlab</category>
    </item>
  </channel>
</rss>
