<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ulises Gascón</title>
    <description>The latest articles on DEV Community by Ulises Gascón (@ulisesgascon).</description>
    <link>https://dev.to/ulisesgascon</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%2F85012%2Fd32d5c70-28d2-4533-a796-85093e62c43a.jpeg</url>
      <title>DEV Community: Ulises Gascón</title>
      <link>https://dev.to/ulisesgascon</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ulisesgascon"/>
    <language>en</language>
    <item>
      <title>How does the Official Node.js News Feeder work?</title>
      <dc:creator>Ulises Gascón</dc:creator>
      <pubDate>Wed, 05 Jul 2023 22:00:28 +0000</pubDate>
      <link>https://dev.to/ulisesgascon/how-does-the-official-nodejs-news-feeder-work-2fa6</link>
      <guid>https://dev.to/ulisesgascon/how-does-the-official-nodejs-news-feeder-work-2fa6</guid>
      <description>&lt;p&gt;Node.js has a &lt;a href="https://nodejs.github.io/nodejs-news-feeder/feed.xml"&gt;new RSS feed&lt;/a&gt; that consolidate all the releases and news from the different teams, working groups and projects inside the org.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Node.js as an organization has too many things ongoing all the time. There are many projects, teams, and working groups working on different things. It is hard to keep track of all the things that are happening, so there is a recurrent need for the community to find a better way to be aware of what is going on. &lt;a href="https://github.com/nodejs/next-10/issues/110"&gt;This discussion&lt;/a&gt; has been going on for a while, and there are many ideas on how to solve this problem, but we decided that RSS is a good way to start as this will also help to promote our activities and achievements outside the Node.js org itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The teams and working groups should be able to add their own news without having to change their way of working (no PRs, forms...)&lt;/li&gt;
&lt;li&gt;The information should be available in a valid RSS feed.&lt;/li&gt;
&lt;li&gt;The feed should be updated automatically, but allow for manual news additions and easy content curation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Decisions made
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use GitHub as the source of truth for the news, so we will use the GitHub API to fetch the relevant information from issues, discussions, releases...&lt;/li&gt;
&lt;li&gt;Use a GitHub Action to generate the RSS feed and publish it to GitHub Pages.&lt;/li&gt;
&lt;li&gt;Use a GitHub Action to update the feed automatically every week or manually when needed, generating a new commit with the changes in a PR that will be reviewed and curated by the team.&lt;/li&gt;
&lt;li&gt;Avoid external dependencies as much as possible, so the solution should be self-contained and easy to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The full source code can be found in &lt;a href="https://github.com/nodejs/nodejs-news-feeder"&gt;this repository&lt;/a&gt;. I will explain the most relevant parts of the solution here.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qb6bZYrI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xwmhw3ie7mcvdsu4cnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qb6bZYrI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2xwmhw3ie7mcvdsu4cnp.png" alt="Architectural overview. Described below" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In general terms, the solution is composed of the following parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The community is the source of the news. They are the ones who reply to specific issues or discussions related to the news feed, as well as manage the new releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Curators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The curators are the ones who review the changes and merge the PRs that update the feed. The feed is automatically updated every week, but it can also be updated manually when needed. There are several scripts in order to collect, process, validate, and publish the feed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Readers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The readers are the ones who consume the feed. They can be humans or bots. The readers can subscribe to the feed using the following URL: &lt;code&gt;https://nodejs.github.io/nodejs-news-feeder/feed.xml&lt;/code&gt;. We provide a Slack channel where the feed is automatically published, so the community can be aware of the latest news.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Structure
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Configuration
&lt;/h4&gt;

&lt;p&gt;There is a &lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/main/config.json"&gt;config.json file&lt;/a&gt; that stores all the references to the external resources (discussions, issues, releases...), API rate limits, and the configuration of the last execution time (&lt;code&gt;lastCheckTimestamp&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The last execution time (&lt;code&gt;lastCheckTimestamp&lt;/code&gt;) will prevent us from including already processed information in the feed. This prevents us from using third-party software or reconciling the feed to avoid duplications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lastCheckTimestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1688584036809&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reposPaginationLimit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"releasePaginationLimit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"commentsPaginationLimit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"breakDelimiter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;/image&amp;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;"discussionsInScope"&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;"issuesInScope"&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;h4&gt;
  
  
  Modularity
&lt;/h4&gt;

&lt;p&gt;The solution is divided into different scripts that do different things, which allows us to reuse the code and make it easier to maintain.&lt;/p&gt;

&lt;p&gt;This structure is clearer by checking &lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/main/package.json"&gt;the package.json&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"collect:releases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scripts/collect-releases.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"collect:issues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scripts/collect-issues.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"collect:discussions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scripts/collect-discussions.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rss:validate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scripts/validate.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rss:build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scripts/build.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rss:format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scripts/format.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"rss:format-check"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node scripts/format-check.js"&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;h3&gt;
  
  
  Fetching content from Github
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Releases
&lt;/h4&gt;

&lt;p&gt;Node.js uses Github Releases to publish new versions of different projects. There are many projects in the organization, and we keep adding more on a regular basis.&lt;/p&gt;

&lt;p&gt;So, &lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/41a76af236538ae07b0a3946e65ab45a8d5fd1ef/scripts/collect-releases.js"&gt;this script&lt;/a&gt; will do the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetch all the repositories in the organization.&lt;/li&gt;
&lt;li&gt;Fetch the latest releases for each repository.&lt;/li&gt;
&lt;li&gt;Filter the releases by the ones that are newer than the last execution time (&lt;code&gt;lastCheckTimestamp&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Format the releases to be included in the feed.&lt;/li&gt;
&lt;li&gt;Add the releases to the feed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Issues
&lt;/h4&gt;

&lt;p&gt;Each project is publishing its news in GitHub Issues as responses.&lt;/p&gt;

&lt;p&gt;So, &lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/41a76af236538ae07b0a3946e65ab45a8d5fd1ef/scripts/collect-issues.js"&gt;this script&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fetches all the comments in the issues that are in scope.&lt;/li&gt;
&lt;li&gt;Filters the comments by the ones that are newer than the last execution time (&lt;code&gt;lastCheckTimestamp&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Formats the comments to be included in the feed.&lt;/li&gt;
&lt;li&gt;Adds the comments to the feed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Discussions
&lt;/h4&gt;

&lt;p&gt;Discussions are very similar to issues, but they are not supported in the GitHub API REST, so we used the &lt;a href="https://docs.github.com/en/graphql"&gt;GitHub GraphQL API&lt;/a&gt; to fetch the comments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;comments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;discussionsInScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;discussionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;team&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;graphql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`
    {
      repository(name: "node", owner: "nodejs") {
        discussion(number: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;discussionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) {
          comments(last: 100) {
            edges {
              node {
                body
                publishedAt
                updatedAt
                databaseId
              }
            }
          }
        }
      }
    }
    `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`token &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discussion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;edges&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishedAt&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;lastCheckTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;team&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;discussionId&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;&lt;em&gt;&lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/main/scripts/collect-releases.js"&gt;See the full file&lt;/a&gt; for more details&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the feed
&lt;/h3&gt;

&lt;p&gt;In order to update the feed we need to split the current feed by a &lt;code&gt;breakDelimiter&lt;/code&gt; that is defined in the config.json file.&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="c1"&gt;//...OMITED...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;feedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFeedContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;feedContent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;breakDelimiter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatedFeedContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;breakDelimiter&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;relevantReleases&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;after&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="nx"&gt;overwriteFeedContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedFeedContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/main/scripts/collect-releases.js"&gt;See the full file&lt;/a&gt; for more details&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Formatting the feed
&lt;/h3&gt;

&lt;p&gt;We use the library &lt;a href="https://www.npmjs.com/package/xml-formatter"&gt;xml-formatter&lt;/a&gt; to normalize the feed content. This will help us curate the content later on when reviewing the PR.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;xmlFormat&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xml-formatter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getFeedContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;overwriteFeedContent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../utils/index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;xml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFeedContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedXml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;xmlFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;indentation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;  &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;collapseContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;overwriteFeedContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formattedXml&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/main/scripts/collect-releases.js"&gt;See the full file&lt;/a&gt; for more details&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate the feed
&lt;/h3&gt;

&lt;p&gt;In order to validate the feed, we directly use the &lt;a href="https://validator.w3.org/feed/check.cgi"&gt;W3C Feed Validation Service&lt;/a&gt; with an HTTP Request, simulating the form (using the &lt;a href="https://www.npmjs.com/package/got"&gt;got&lt;/a&gt; library) and parsing the response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;got&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://validator.w3.org/feed/check.cgi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;rawdata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;xml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;manual&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="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="c1"&gt;// Avoid importing CSS in the document&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;JSDOM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/@import.*/gm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recommendations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ul&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recommendations&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;title&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sorry&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🚨 Feed is invalid!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&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="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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Feed is valid!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: In order to use the library &lt;a href="https://www.npmjs.com/package/jsdom"&gt;jsdom&lt;/a&gt; to scrape the HTML response we need to avoid the &lt;code&gt;@import&lt;/code&gt; statements in the CSS.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Github Action
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Cron Job and Manual Trigger
&lt;/h4&gt;

&lt;p&gt;The GitHub Action is configured to run every week, but it can be triggered manually by using the &lt;code&gt;workflow_dispatch&lt;/code&gt; event. This is useful when we want to update the feed manually, for example when we want to add a new news that is not available on GitHub or just want to promote some news quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&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;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0'&lt;/span&gt;
&lt;span class="c1"&gt;# ...OMITED... &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/main/.github/workflows/populate_feed.yml"&gt;See the full file&lt;/a&gt; for more details&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  API Limits
&lt;/h4&gt;

&lt;p&gt;The GitHub API has a limit on requests. This process makes many requests to the API, so the best way to overcome this limitation is by using a GitHub Token.&lt;/p&gt;

&lt;p&gt;This token can be created by a user and then added to the repository secrets. The GitHub Action will use this token to authenticate the requests to the API, and it will have a higher limit than the anonymous requests.&lt;/p&gt;

&lt;p&gt;But the best solution is to use the already available tokens in the GitHub Actions as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ...OMITED...  &lt;/span&gt;
&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;pull-requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;none&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# ...OMITED...  &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;Collect Releases&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;npm run collect:releases&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

    &lt;span class="c1"&gt;# ...OMITED...  &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/nodejs/nodejs-news-feeder/blob/main/.github/workflows/populate_feed.yml"&gt;See the full file&lt;/a&gt; for more details&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We are passing the &lt;code&gt;secrets.GITHUB_TOKEN&lt;/code&gt; as an environment variable &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; to the scripts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Slack Notifications
&lt;/h3&gt;

&lt;p&gt;The feed is published on Slack using the &lt;a href="https://slack.com/services/B04V1KBNVSB"&gt;RSS App&lt;/a&gt;. This app is listening to the feed and pushing the new items to specific channel(s). In our case, we are using the channel &lt;code&gt;#nodejs-news-feed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zaaGP_Es--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eukgyqjafebkodabg7nx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zaaGP_Es--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eukgyqjafebkodabg7nx.png" alt="Slack Node.js News Feeder channel screenshot that is showing the feed items published in the channel including a fancy preview of the releases." width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgment
&lt;/h2&gt;

&lt;p&gt;Thanks a lot to the &lt;a href="https://github.com/nodejs/next-10/#team-members"&gt;Node.js Next 10 team&lt;/a&gt; for the support and feedback on this project, especially to &lt;a href="https://github.com/mhdawson"&gt;Michael Dawson&lt;/a&gt; for the guidelines, reviews, and suggestions.&lt;/p&gt;

</description>
      <category>node</category>
      <category>rss</category>
      <category>githubactions</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dockerize Javascript IOT Applications</title>
      <dc:creator>Ulises Gascón</dc:creator>
      <pubDate>Mon, 03 Jul 2023 21:52:23 +0000</pubDate>
      <link>https://dev.to/ulisesgascon/dockerize-javascript-iot-applications-4327</link>
      <guid>https://dev.to/ulisesgascon/dockerize-javascript-iot-applications-4327</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O24DfsDU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bq9t1euwg1wtso6szrb1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O24DfsDU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bq9t1euwg1wtso6szrb1.jpg" alt="I am eating my breakfast while doing IOT Development in a Coffee Shop" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I am a &lt;a href="https://www.docker.com/captains/ulises-gascon/"&gt;Docker Captain&lt;/a&gt; and I love build Robots and IoT devices.&lt;/p&gt;

&lt;p&gt;In those years I have been doing a lot of IoT prototypes for companies and for myself. I have been using Arduino, Raspberry Pi, and other microcontrollers.&lt;/p&gt;

&lt;p&gt;There are few things that make IoT development very different from other types of software development. One of them is that you need to have a physical device to test your code or build a good simulator. This means that you need to have a device that you can connect to your computer and run your code on it.&lt;/p&gt;

&lt;p&gt;It was very common for me to have a lot of devices, sensors and wires in my backpack while commuting to work. I end up having many funny situations when I was doing IoT development in Coffee Shops around the world, just like the picture above and &lt;a href="https://twitter.com/kom_256/status/679580650307977216/photo/1"&gt;many others&lt;/a&gt; from 2015.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;When Arduino started to become popular, I was very excited about the idea of building my own IoT devices. I started to learn how to program in C/C++ and I bought a lot of sensors and other components to build my own projects.&lt;/p&gt;

&lt;p&gt;But I soon realized that I was spending too much time on the Arduino source code, so I decided to use Javascript instead. I started to use &lt;a href="https://johnny-five.io/"&gt;Johnny-Five&lt;/a&gt; and I was able to build complex IoT device in a few hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prototype is the new MVP
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ljqHDzBX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3wwaoi35jevkdv0s241x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ljqHDzBX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3wwaoi35jevkdv0s241x.jpg" alt="Very simple IOT device with a RGB light controlled with JS in a candy jar" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While the &lt;a href="https://twitter.com/fictiziaescuela/status/726083557601497088"&gt;JSDayES 2016&lt;/a&gt; was taking place, I was in one of the stands. During the breaks I was able to hack the welcome candy jar and build a simple IoT device that was controlled by a Node.js server. The device was able to change the color of the light depending on the logic that you define in &lt;a href="https://opensource.ulisesgascon.com/jsdayes-golosinas-iot"&gt;a simple Node.js App&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Johnny Five is so cool?
&lt;/h2&gt;

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

&lt;p&gt;The library &lt;a href="https://johnny-five.io/"&gt;Johnny-Five&lt;/a&gt; is a Javascript library that allows you to control Arduino and other microcontrollers using Javascript. It is very easy to use and it has a lot of examples that you can use to learn how to use it.&lt;/p&gt;

&lt;p&gt;As a Javascript developer, I was very happy to be able to use the same language to build IoT devices:&lt;/p&gt;

&lt;p&gt;The typical blink led example for an Arduino board looks like this:&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;var&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;johnny-five&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Board&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;led&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;five&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Led&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;led&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In compare to the classic approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// the setup function runs once when you press reset or power the board&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// initialize digital pin LED_BUILTIN as an output.&lt;/span&gt;
  &lt;span class="n"&gt;pinMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OUTPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// the loop function runs over and over again forever&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HIGH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// turn the LED on (HIGH is the voltage level)&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                      &lt;span class="c1"&gt;// wait for a second&lt;/span&gt;
  &lt;span class="n"&gt;digitalWrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LED_BUILTIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LOW&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// turn the LED off by making the voltage LOW&lt;/span&gt;
  &lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;                      &lt;span class="c1"&gt;// wait for a second&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might not look like a big difference, but when you are building complex IoT devices, it makes a big difference. You can use all the Javascript features like Promises, Async/Await, etc. to build your IoT devices and all the compute power that the host device provides, as well all the modern communication that we expect today (HTTP APIS, SDKs..).&lt;/p&gt;

&lt;p&gt;This will force you to connect (USB, Bluetooth...) the IoT device to a computer all the time as the device won't have the instructions recorded.&lt;/p&gt;

&lt;p&gt;Obviously not all the IOT projects fits this requirements, but for a simple prototype is a fantastic solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should I Dockerize my project?
&lt;/h2&gt;

&lt;p&gt;Johnny Five is a fantastic library, but some of their dependencies have a complex installation process (under the hood), this sometimes makes difficult to install the library in some environments. Specially if you need to change between devices, it was very frustrating for me at the beginning until I decided to use Docker to make it easier to execute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerize your JS IoT application
&lt;/h2&gt;

&lt;p&gt;I created this &lt;a href="https://github.com/UlisesGascon/POC-docker-johnny-five"&gt;repository&lt;/a&gt; to dockerize the blink example, so it is easier to follow and explore the details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get the necessary software
&lt;/h3&gt;

&lt;p&gt;Before you begin, you need to have &lt;a href="https://docs.docker.com/engine/install/"&gt;Docker installed&lt;/a&gt; on your machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Write a Dockerfile
&lt;/h3&gt;

&lt;p&gt;Here is a hyper simplified Dockerfile, if you want to build a production ready example &lt;a href="https://nodejs.org/en/docs/guides/nodejs-docker-webapp"&gt;Start from this&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you speak Spanish I also recommend you my ebook &lt;a href="https://dockerseguro.ulisesgascon.com/"&gt;"Docker Seguro"&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18.14.1&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /usr/src/app&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;udev
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--unsafe-perm&lt;/span&gt; &lt;span class="nt"&gt;--build-from-source&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;serialport
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; [ "npm", "run", "start" ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build the Docker image
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;docker build&lt;/code&gt; command, create a Docker image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; blink &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run your application in a Docker container
&lt;/h3&gt;

&lt;p&gt;Launch your application using the &lt;code&gt;docker run&lt;/code&gt; command, specifying the necessary parameters to configure your container.&lt;/p&gt;

&lt;p&gt;Here is the tricky part, you will need to pass the USB device to the container, so it can access the Arduino board.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--privileged&lt;/span&gt; &lt;span class="nt"&gt;--device&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/ttyUSB0 blink
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case the port was &lt;code&gt;/dev/ttyUSB0&lt;/code&gt;, but this might be different for you. In this &lt;a href="https://www.mathworks.com/help/supportpkg/arduinoio/ug/find-arduino-port-on-windows-mac-and-linux.html"&gt;Guide&lt;/a&gt; you can find more information.&lt;/p&gt;

&lt;p&gt;Sometimes access to USB devices from Docker can be tricky, especially in macOS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I pass through a USB device to a container?&lt;/strong&gt;&lt;br&gt;
Unfortunately, it is not possible to pass through a USB device (or a serial port) to a container as it requires support at the hypervisor level.&lt;br&gt;
&lt;a href="https://docs.docker.com/docker-for-mac/faqs/#can-i-pass-through-a-usb-device-to-a-container"&gt;Docker: General FAQs for Desktop&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But there are many workarounds from the community:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/a/24231872"&gt;Docker - a way to give access to a host USB or serial device?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mil.ad/blog/2018/access-usb-devices-in-container-in-mac.html"&gt;How to use a USB device in a Docker container on Mac&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Publish your Docker image
&lt;/h3&gt;

&lt;p&gt;Optionally, you can publish your Docker image to a container registry, such as Docker Hub, for easy access and distribution.&lt;/p&gt;

</description>
      <category>iot</category>
      <category>docker</category>
      <category>arduino</category>
      <category>node</category>
    </item>
    <item>
      <title>Safely store secrets in Git using Blackbox</title>
      <dc:creator>Ulises Gascón</dc:creator>
      <pubDate>Mon, 06 Feb 2023 17:46:09 +0000</pubDate>
      <link>https://dev.to/ulisesgascon/safely-store-secrets-in-git-using-blackbox-419c</link>
      <guid>https://dev.to/ulisesgascon/safely-store-secrets-in-git-using-blackbox-419c</guid>
      <description>&lt;p&gt;Storing secrets in a Git repository can be a dangerous proposition, as the information is often stored in plaintext and can be seen by anyone with access to the repository. It's important to remember that secrets should never be stored in plaintext and that measures should be taken to ensure that the secrets are encrypted and secure.&lt;/p&gt;

&lt;p&gt;In this post, I will explain how to use &lt;a href="https://github.com/StackExchange/blackbox" rel="noopener noreferrer"&gt;Blackbox&lt;/a&gt; to safely store secrets within a Git repository as well as provide examples of how to use it in GitHub Actions as a secret repository to quickly load secrets into your pipelines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blackbox in a nutshell
&lt;/h2&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/beJT7wMvJo4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Most developers understand the importance of keeping secrets safe and secure. Unfortunately, it can be difficult to achieve this when dealing with secrets that are stored in a Git repository. Fortunately, Blackbox is a great tool for securely storing secrets within Git. It helps streamline the process of working with secrets while ensuring the highest level of security possible. With its easy-to-use PGP encryption and automated processes, Blackbox ensures developers have a safe and efficient way of managing their secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalation
&lt;/h2&gt;

&lt;p&gt;In mac the best way is to use &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;Brew&lt;/a&gt;, so you should have Brew installed in your machine, in the first place.&lt;/p&gt;

&lt;p&gt;Then, with this command, install Blackbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;blackbox
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case that you are using a different platform please check the &lt;a href="https://github.com/StackExchange/blackbox#installation-instructions" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Initialize the project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;blackbox_initialize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add users
&lt;/h3&gt;

&lt;p&gt;♦️ Note: you need to have the user's pgp public key in your pgp keychain and you MUST be an ADMIN to run this command (you can't add yourself unless you are the first user in the project)&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="c"&gt;# Add the new key to the project&lt;/span&gt;
blackbox_addadmin &lt;span class="o"&gt;{&lt;/span&gt;email&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Re-encrypt the files including the new user&lt;/span&gt;
blackbox_update_all_files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Remove users
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove the user from the project&lt;/span&gt;
blackbox_removeadmin &lt;span class="o"&gt;{&lt;/span&gt;email&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# Re-encrypt the files without the previous key&lt;/span&gt;
blackbox_update_all_files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add a new file
&lt;/h3&gt;

&lt;p&gt;By default, Backbox won't encrypt the new files, so you need to add them manually by using &lt;code&gt;blackbox_register_new_file&lt;/code&gt; once your changes are done.&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="c"&gt;# Create the file&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;secrets/demo_file.txt
&lt;span class="c"&gt;# ... do your things in the file...&lt;/span&gt;
&lt;span class="c"&gt;# Encrypt the file&lt;/span&gt;
blackbox_register_new_file secrets/demo_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;♦️ Note: when you encrypt the file, the file is renamed with a &lt;code&gt;.gpg&lt;/code&gt; like &lt;code&gt;secrets/demo_file.txt.gpg&lt;/code&gt; extension. Now the content is not legible (encrypted). When the file is encrypted, you are ready to commit the changes over that file :-)&lt;/p&gt;

&lt;h3&gt;
  
  
  Edit a file
&lt;/h3&gt;

&lt;p&gt;You need to decrypt the file first and then re-encrypt the file at the end.&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="c"&gt;# Decrypt the file&lt;/span&gt;
blackbox_decrypt_file secrets/demo_file.txt
&lt;span class="c"&gt;# ... make your changes in the file...&lt;/span&gt;
&lt;span class="c"&gt;# Re-encrypt the file&lt;/span&gt;
blackbox_edit_end secrets/demo_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the file is decrypted, it will appear as a new file with the same name without the &lt;code&gt;.pgp&lt;/code&gt; extension. The file now looks like this &lt;code&gt;secrets/demo_file.txt&lt;/code&gt; and the content is in clear text (decrypted).&lt;/p&gt;

&lt;p&gt;You can perform all the changes you want and then encrypt the file again when you are done. Then the plain file will be automatically removed and the encrypted file updated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delete a file
&lt;/h3&gt;

&lt;p&gt;Use the command &lt;code&gt;blackbox_deregister_file  &amp;lt;file&amp;gt;&lt;/code&gt;. Afterwards, is very important to run the command &lt;code&gt;blackbox_shred_all_files&lt;/code&gt; in order to remove decrypted files that could still exist in your machine. (Be aware: there is no typo in the command, the 'a' letter is NOT missed from the &lt;code&gt;shred&lt;/code&gt; word).&lt;/p&gt;

&lt;h3&gt;
  
  
  Decrypt all files
&lt;/h3&gt;

&lt;p&gt;If you need to perform changes in several files, the easiest way is to decrypt all the files at once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;blackbox_decrypt_all_files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Afterwards, when you're done, please manually encrypt all the files, one by one, with the command &lt;code&gt;blackbox_edit_end secrets/{file_name}&lt;/code&gt;.&lt;br&gt;
There is no option to batch that process yet in Blackbox, but there is a &lt;a href="https://github.com/StackExchange/blackbox/issues/52" rel="noopener noreferrer"&gt;discussion ongoing about it&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check the differences between a clear file and a encrypted file:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;blackbox_diff secrets/demo_file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Blackbox in GitHub Actions
&lt;/h2&gt;

&lt;p&gt;One of the coolest features is that you can use pgp to decrypt secrets in any CI process, this is a simple example that&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load the PGP keys in the machine&lt;/li&gt;
&lt;li&gt;Clone the secret management repository using a github token&lt;/li&gt;
&lt;li&gt;Decrypt one file and store it as &lt;code&gt;.env&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Source the &lt;code&gt;.env&lt;/code&gt; file to the &lt;code&gt;npm run build&lt;/code&gt; process.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pull Request Check&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&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;Ensure Node Version&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v3&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;import GPG key&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GPG_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GPG_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo $GPG_KEY | base64 --decode &amp;gt; signature.asc&lt;/span&gt;
          &lt;span class="s"&gt;gpg --batch --import signature.asc&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;Clone secrets-management&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GH_EXTENDED_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GH_EXTENDED_TOKEN }}&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;git clone https://${GH_EXTENDED_TOKEN}:x-oauth-basic@github.com/UlisesGascon/super-secrets-management.git&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;decrypt secrets (local environment)&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;BRANCH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.vars.outputs.short_ref }}&lt;/span&gt;
          &lt;span class="na"&gt;GPG_PASSPHRASE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GPG_PASSPHRASE }}&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;gpg --no-tty --batch --passphrase "$GPG_PASSPHRASE" --pinentry-mode loopback --output .env --decrypt super-secrets-management/secrets/app/.env.gpg&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&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;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build the project&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;source .env&lt;/span&gt;
          &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="c1"&gt;#... MORE STEPS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://github.com/StackExchange/blackbox" rel="noopener noreferrer"&gt;Blackbox&lt;/a&gt; is a great tool for securely storing secrets within a Git repository. It provides an easy-to-use PGP encryption system, which ensures that secrets are kept safe and secure. Furthermore, Blackbox can also be used in GitHub Actions as a secret repository, allowing developers to quickly load secrets into their pipelines. With its robust security and automated processes, Blackbox is an excellent tool for securely storing secrets within Git.&lt;/p&gt;

</description>
      <category>security</category>
      <category>crypto</category>
      <category>phishing</category>
      <category>privacy</category>
    </item>
    <item>
      <title>You should use the OpenSSF Scorecard</title>
      <dc:creator>Ulises Gascón</dc:creator>
      <pubDate>Mon, 23 Jan 2023 20:46:00 +0000</pubDate>
      <link>https://dev.to/ulisesgascon/you-should-use-the-openssf-scorecard-4eh4</link>
      <guid>https://dev.to/ulisesgascon/you-should-use-the-openssf-scorecard-4eh4</guid>
      <description>&lt;p&gt;As you may know, the Node.js Ecosystem Security Working Group has defined &lt;a href="https://github.com/nodejs/security-wg#current-initiatives" rel="noopener noreferrer"&gt;its priorities for 2023&lt;/a&gt;. A key initiative for us will be to assess the organization against the best practices available, such as the OpenSSF Scorecard.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenSSF in Node.js
&lt;/h2&gt;

&lt;p&gt;We had a great discussion about the OpenSSF scorecard with the Google Open Source Security Team (GOSST) in the Ecosystem Security Working Group meeting this week.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/qzLZbdHSfZE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We began the discussion in &lt;a href="https://github.com/nodejs/security-wg/issues/851" rel="noopener noreferrer"&gt;this issue&lt;/a&gt;, and here you can find &lt;a href="https://github.com/nodejs/security-wg/blob/main/meetings/2023-01-19.md" rel="noopener noreferrer"&gt;the meeting notes&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Assessment against best practices (OpenSSF Scorecards ...) &lt;a href="https://github.com/nodejs/security-wg/issues/859" rel="noopener noreferrer"&gt;#859&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add OSSF Scorecard &lt;a href="https://github.com/nodejs/security-wg/issues/851" rel="noopener noreferrer"&gt;#851&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discussion with GOSST about implementing it on Node.js&lt;/li&gt;
&lt;li&gt;The Nodejs currently report is located &lt;a href="https://deps.dev/project/github/nodejs%2Fnode" rel="noopener noreferrer"&gt;here&lt;/a&gt;, also &lt;a href="https://api.securityscorecards.dev/projects/github.com/nodejs/node" rel="noopener noreferrer"&gt;json version available&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Agreement to update action version tag by hash in GHA, following &lt;a href="https://app.stepsecurity.io/secureworkflow/nodejs/node/coverage-linux.yml/main?enable=pin" rel="noopener noreferrer"&gt;this example&lt;/a&gt;, lead by GOSST&lt;/li&gt;
&lt;li&gt;Agreement to add/document the next steps in &lt;a href="https://github.com/nodejs/security-wg/issues/859" rel="noopener noreferrer"&gt;this issue&lt;/a&gt; in order to provide a good context for the following PRs and TSC Meetings, lead by GOSST&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The current score for Node.js is &lt;a href="https://deps.dev/project/github/nodejs%2Fnode" rel="noopener noreferrer"&gt;6.8 out of 10&lt;/a&gt;. We will be working to improve this score in the coming months. If you would like to be notified, please subscribe to &lt;a href="https://github.com/nodejs/security-wg/" rel="noopener noreferrer"&gt;the Security Working Group repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenSSF Scorecard in a nutshell
&lt;/h2&gt;

&lt;p&gt;The Scorecard will evaluate the security of your project based on automated checks related to four scenarios.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Malicious maintainers &lt;/li&gt;
&lt;li&gt;Build System Compromises&lt;/li&gt;
&lt;li&gt;Source Code Compromises&lt;/li&gt;
&lt;li&gt;Malicious packages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In order to accomplish this, the scripts are focused in 5 areas (Code Vulnerabilities, Maintenance, Continuous Testing, Source Risk Assestment, Build Risk Assestment and Holistic Security Practices). &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%2Fblog.ulisesgascon.com%2Fimg%2Fdiagram-flower.svg" 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%2Fblog.ulisesgascon.com%2Fimg%2Fdiagram-flower.svg" alt="Diagram flower that includes the 5 areas with the Holistic Security Practices in the middle" width="602" height="583"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each area has its own associated risk, so the overall score is the average of the five areas. &lt;a href="https://github.com/ossf/scorecard/blob/main/docs/checks.md" rel="noopener noreferrer"&gt;Here&lt;/a&gt;, you can check the details of each by consulting the documentation in detail.&lt;/p&gt;

&lt;p&gt;The following are the types of questions this score card provides answers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the project contain a security policy?&lt;/li&gt;
&lt;li&gt;Does the project use fuzzing tools?&lt;/li&gt;
&lt;li&gt;Does the project use static code analysis tools?&lt;/li&gt;
&lt;li&gt;Does the project use Branch Protection?&lt;/li&gt;
&lt;li&gt;Does the project have contributors from at least two different organizations?&lt;/li&gt;
&lt;li&gt;Does the project cryptographically sign releases?&lt;/li&gt;
&lt;li&gt;And many more...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are wondering if this is a good idea for your project, I think it is a good idea to at least review your packages in &lt;a href="https://deps.dev/" rel="noopener noreferrer"&gt;the directory&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenSSF Scorecard Implementation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;It took less than 5 minutes to install. It quickly analysed the repo and identified easy ways to make the project more secure. Priya Wadhwa, &lt;a href="https://github.com/GoogleContainerTools/kaniko" rel="noopener noreferrer"&gt;Kaniko&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://securityscorecards.dev/#using-the-github-action" rel="noopener noreferrer"&gt;Using the GitHub Action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://securityscorecards.dev/#using-the-cli" rel="noopener noreferrer"&gt;Using the CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;👉 This blog post was originally posted in &lt;a href="https://blog.ulisesgascon.com/openssf-scorecard-in-nodejs" rel="noopener noreferrer"&gt;my blog&lt;/a&gt; the 22/01/2023&lt;/p&gt;

</description>
      <category>security</category>
      <category>node</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
