<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Bassem - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Bassem - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 22:20:02 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/bassemdy/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build Your First JavaScript GitHub Action ]]>
                </title>
                <description>
                    <![CDATA[ I love working with GitHub Actions. They're easy to use yet so powerful. I'm especially excited when I see how creative people are when using them to automate different tasks. I want you to have that same power. That's why I'm going to show you how t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-your-first-javascript-github-action/</link>
                <guid isPermaLink="false">66bb92ac867a396452a8028b</guid>
                
                    <category>
                        <![CDATA[ GitHub Actions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bassem ]]>
                </dc:creator>
                <pubDate>Mon, 10 Jan 2022 17:36:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/banner-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I love working with <a target="_blank" href="https://docs.github.com/en/actions">GitHub Actions</a>. They're easy to use yet so powerful. I'm especially excited when I see how creative people are when using them to automate different tasks.</p>
<p>I want you to have that same power. That's why I'm going to show you how to create your first custom JavaScript action in just few steps.</p>
<p>Let's dig in.</p>
<h2 id="heading-what-is-a-github-action">What is a GitHub Action?</h2>
<p>First, we need to establish the distinction between "GitHub Actions" and an "Action". The former is the product's name and the latter is custom code that you can include in a workflow job as a step to accomplish a task.</p>
<p>For example, an action can publish your code to a package manager like <a target="_blank" href="https://www.npmjs.com/">npm</a> or <a target="_blank" href="https://yarnpkg.com/">yarn</a>. It can also integrate with an SMS service provider to alert you when an urgent issue is created in your repo. Or it can turn on your coffee machine when you create a new pull request. </p>
<p>The possibilities are endless for what you can do!</p>
<h2 id="heading-what-are-the-components-of-github-actions">What are the components of GitHub Actions?</h2>
<p>Before we get started writing code, it's important for us to understand the building blocks of GitHub Actions.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/build-your-first-github-action_components.png" alt="build-your-first-github-action_components" width="600" height="400" loading="lazy"></p>
<p>Let's break down this diagram, starting from the left and going right:</p>
<ol>
<li><strong>Event</strong>: This is the event that triggers the action. It represents an activity in the repository that will trigger a workflow run.</li>
<li><strong>Workflow</strong>: This is the workflow that is run when the event occurs.</li>
<li><strong>Job</strong>: A set of steps that are run in sequence to accomplish a task. Each job runs on its own runner.</li>
<li><strong>Step</strong>: A step is either a shell script, or an action that will be run on the runner assigned for the job the step is part of.</li>
<li><strong>Runner</strong>: A runner is a virtual machine (or any computer with a supported operating system) that runs the steps in a job.</li>
</ol>
<p>This is very well explained in GitHub's extensive docs, and you can read more about the components <a target="_blank" href="https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#the-components-of-github-actions">here</a>.</p>
<h2 id="heading-when-do-i-need-to-create-an-action">When do I need to create an Action?</h2>
<p>Since each step can be either a shell script or an action, how do we decide which option to choose?</p>
<p>If you answer "yes" to any of the below questions, then you're better off creating an Action:</p>
<ol>
<li>Will other people benefit from the action you're creating and actually reuse it?</li>
<li>Do you need to build complex logic that cannot be written in a shell script?</li>
<li>Are you going to be using any third-party libraries?</li>
<li>Do you need to make API calls to a third-party service?</li>
<li>Do you have the ability to maintain this code and release fixes or updates?</li>
<li>Do you need to be able to run this action on different operating systems?</li>
<li>Are you proficient in JavaScript but not Bash or PowerShell?</li>
<li>Do you want to learn how to make one?</li>
</ol>
<h2 id="heading-lets-create-our-action">Let's create our Action</h2>
<p>We're going to build an Action that will create a comment whenever a pull request is opened on our repository and add labels depending on the file types changed. The comment will contain a summary of the changes introduced in the pull request.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/build-your-first-github-preview.png" alt="build-your-first-github-preview" width="600" height="400" loading="lazy"></p>
<h3 id="heading-1-create-an-empty-public-repository">1. Create an empty public repository</h3>
<p>Let's start by creating an empty GitHub repository called: <code>PR-metadata-action</code>. This will be the repository that we will use to store our Action.</p>
<p>It has to be public, otherwise we won't be able to use it in our workflows.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/build-your-first-github-action_newrepo-1.png" alt="build-your-first-github-action_newrepo-1" width="600" height="400" loading="lazy"></p>
<h3 id="heading-2-clone-the-repository-locally-and-initialize-a-node-project">2. Clone the repository locally and initialize a Node project</h3>
<p>Go to the folder where you wish to store the Action's repository. Then let's clone the repository on our machine:</p>
<pre><code class="lang-bash">$ git <span class="hljs-built_in">clone</span> git@github.com:Link-/PR-metadata-action.git
Cloning into <span class="hljs-string">'PR-metadata-action'</span>...
remote: Enumerating objects: 4, <span class="hljs-keyword">done</span>.
remote: Counting objects: 100% (4/4), <span class="hljs-keyword">done</span>.
remote: Compressing objects: 100% (4/4), <span class="hljs-keyword">done</span>.
Receiving objects: 100% (4/4), <span class="hljs-keyword">done</span>.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
</code></pre>
<p>Inside our newly created repository's folder, let's initialize a new Node.js project:</p>
<pre><code class="lang-bash">$ <span class="hljs-built_in">cd</span> PR-metadata-action/
$ npm init -y
Wrote to /Users/link-/PR-metadata-action/package.json:

{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"pr-metadata-action"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"description"</span>: <span class="hljs-string">"Adds pull request file changes as a comment to a newly opened PR"</span>,
  <span class="hljs-string">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-string">"repository"</span>: {
    <span class="hljs-string">"type"</span>: <span class="hljs-string">"git"</span>,
    <span class="hljs-string">"url"</span>: <span class="hljs-string">"git+https://github.com/Link-/PR-metadata-action.git"</span>
  },
  <span class="hljs-string">"keywords"</span>: [],
  <span class="hljs-string">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-string">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-string">"bugs"</span>: {
    <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://github.com/Link-/PR-metadata-action/issues"</span>
  },
  <span class="hljs-string">"homepage"</span>: <span class="hljs-string">"https://github.com/Link-/PR-metadata-action#readme"</span>
}
</code></pre>
<h3 id="heading-3-create-an-action-metadata-file">3. Create an Action metadata file</h3>
<p>Let's create <code>action.yml</code>. This file is very important, as it will define the <code>interface</code> of our Action:</p>
<ul>
<li><strong>inputs</strong>: the parameters containing data that the action expects to use during runtime</li>
<li><strong>outputs</strong>: data that an action sets after it's done. We're not going to have an output for our action this time.</li>
<li><strong>runs</strong>: specifies the execution runtime of the action, which will be node16 in this case</li>
</ul>
<p>Read more about the <a target="_blank" href="https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions">metadata file syntax</a>.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">'PR Metadata Action'</span>
<span class="hljs-attr">description:</span> <span class="hljs-string">'Adds pull request file changes as a comment to a newly opened PR'</span>
<span class="hljs-attr">inputs:</span>
  <span class="hljs-attr">owner:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'The owner of the repository'</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">repo:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'The name of the repository'</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">pr_number:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'The number of the pull request'</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">token:</span>
    <span class="hljs-attr">description:</span> <span class="hljs-string">'The token to use to access the GitHub API'</span>
    <span class="hljs-attr">required:</span> <span class="hljs-literal">true</span>
<span class="hljs-attr">runs:</span>
  <span class="hljs-attr">using:</span> <span class="hljs-string">'node16'</span>
  <span class="hljs-attr">main:</span> <span class="hljs-string">'index.js'</span>
</code></pre>
<h3 id="heading-4-add-actions-toolkit-packages">4. Add Actions toolkit packages</h3>
<p>GitHub has created an open source software development toolkit (SDK) that will make your life much easier when creating actions.</p>
<p>The 2 main packages we will be using today are:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/core">@actions/core</a>: this package contains the core functionality of the Action, such as the <code>context</code> object that contains the information about the current run, the <code>inputs</code> object that contains the parameters of the action, and the <code>outputs</code> object that will contain the data that the action sets after it's done.</p>
</li>
<li><p><a target="_blank" href="https://github.com/actions/toolkit/tree/main/packages/github">@actions/github</a>: this package contains the GitHub API REST client that we will use to interact with the GitHub API.</p>
</li>
</ul>
<pre><code class="lang-bash">$ npm install @actions/core
added 3 packages, and audited 4 packages <span class="hljs-keyword">in</span> 1s

found 0 vulnerabilities

$ npm install @actions/github
added 21 packages, and audited 25 packages <span class="hljs-keyword">in</span> 1s

found 0 vulnerabilities
</code></pre>
<p>Our folder structure should look like this now:</p>
<pre><code class="lang-bash">/Users/link-/PR-metadata-action
├── LICENSE
├── README.md
├── action.yml
├── node_modules
├── package-lock.json
└── package.json

1 directory, 6 files
</code></pre>
<h3 id="heading-5-write-the-action">5. Write the Action</h3>
<p>Creating a <code>.gitignore</code> file is important at this stage to avoid pushing unnecessary files to the repository.</p>
<p>A great tool I frequently use is: <a target="_blank" href="https://www.toptal.com/developers/gitignore">https://www.toptal.com/developers/gitignore</a></p>
<p>My <code>.gitignore</code> file is:</p>
<pre><code class="lang-text">https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,node
</code></pre>
<p>Create one that is specific to your environment and project.</p>
<p>We're finally ready to create our <code>index.js</code> file. This is where all the logic of our action will be. We can definitely have a more complex structure, but for now one file will do.</p>
<p>I've commented all the code below so that you know what's happening step by step.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> core = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@actions/core'</span>);
<span class="hljs-keyword">const</span> github = <span class="hljs-built_in">require</span>(<span class="hljs-string">'@actions/github'</span>);

<span class="hljs-keyword">const</span> main = <span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">/**
     * We need to fetch all the inputs that were provided to our action
     * and store them in variables for us to use.
     **/</span>
    <span class="hljs-keyword">const</span> owner = core.getInput(<span class="hljs-string">'owner'</span>, { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span> });
    <span class="hljs-keyword">const</span> repo = core.getInput(<span class="hljs-string">'repo'</span>, { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span> });
    <span class="hljs-keyword">const</span> pr_number = core.getInput(<span class="hljs-string">'pr_number'</span>, { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span> });
    <span class="hljs-keyword">const</span> token = core.getInput(<span class="hljs-string">'token'</span>, { <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span> });

    <span class="hljs-comment">/**
     * Now we need to create an instance of Octokit which will use to call
     * GitHub's REST API endpoints.
     * We will pass the token as an argument to the constructor. This token
     * will be used to authenticate our requests.
     * You can find all the information about how to use Octokit here:
     * https://octokit.github.io/rest.js/v18
     **/</span>
    <span class="hljs-keyword">const</span> octokit = <span class="hljs-keyword">new</span> github.getOctokit(token);

    <span class="hljs-comment">/**
     * We need to fetch the list of files that were changes in the Pull Request
     * and store them in a variable.
     * We use octokit.paginate() to automatically loop over all the pages of the
     * results.
     * Reference: https://octokit.github.io/rest.js/v18#pulls-list-files
     */</span>
    <span class="hljs-keyword">const</span> { <span class="hljs-attr">data</span>: changedFiles } = <span class="hljs-keyword">await</span> octokit.rest.pulls.listFiles({
      owner,
      repo,
      <span class="hljs-attr">pull_number</span>: pr_number,
    });


    <span class="hljs-comment">/**
     * Contains the sum of all the additions, deletions, and changes
     * in all the files in the Pull Request.
     **/</span>
    <span class="hljs-keyword">let</span> diffData = {
      <span class="hljs-attr">additions</span>: <span class="hljs-number">0</span>,
      <span class="hljs-attr">deletions</span>: <span class="hljs-number">0</span>,
      <span class="hljs-attr">changes</span>: <span class="hljs-number">0</span>
    };

    <span class="hljs-comment">// Reference for how to use Array.reduce():</span>
    <span class="hljs-comment">// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce</span>
    diffData = changedFiles.reduce(<span class="hljs-function">(<span class="hljs-params">acc, file</span>) =&gt;</span> {
      acc.additions += file.additions;
      acc.deletions += file.deletions;
      acc.changes += file.changes;
      <span class="hljs-keyword">return</span> acc;
    }, diffData);

    <span class="hljs-comment">/**
     * Loop over all the files changed in the PR and add labels according 
     * to files types.
     **/</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> file <span class="hljs-keyword">of</span> changedFiles) {
      <span class="hljs-comment">/**
       * Add labels according to file types.
       */</span>
      <span class="hljs-keyword">const</span> fileExtension = file.filename.split(<span class="hljs-string">'.'</span>).pop();
      <span class="hljs-keyword">switch</span>(fileExtension) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">'md'</span>:
          <span class="hljs-keyword">await</span> octokit.rest.issues.addLabels({
            owner,
            repo,
            <span class="hljs-attr">issue_number</span>: pr_number,
            <span class="hljs-attr">labels</span>: [<span class="hljs-string">'markdown'</span>],
          });
        <span class="hljs-keyword">case</span> <span class="hljs-string">'js'</span>:
          <span class="hljs-keyword">await</span> octokit.rest.issues.addLabels({
            owner,
            repo,
            <span class="hljs-attr">issue_number</span>: pr_number,
            <span class="hljs-attr">labels</span>: [<span class="hljs-string">'javascript'</span>],
          });
        <span class="hljs-keyword">case</span> <span class="hljs-string">'yml'</span>:
          <span class="hljs-keyword">await</span> octokit.rest.issues.addLabels({
            owner,
            repo,
            <span class="hljs-attr">issue_number</span>: pr_number,
            <span class="hljs-attr">labels</span>: [<span class="hljs-string">'yaml'</span>],
          });
        <span class="hljs-keyword">case</span> <span class="hljs-string">'yaml'</span>:
          <span class="hljs-keyword">await</span> octokit.rest.issues.addLabels({
            owner,
            repo,
            <span class="hljs-attr">issue_number</span>: pr_number,
            <span class="hljs-attr">labels</span>: [<span class="hljs-string">'yaml'</span>],
          });
      }
    }

    <span class="hljs-comment">/**
     * Create a comment on the PR with the information we compiled from the
     * list of changed files.
     */</span>
    <span class="hljs-keyword">await</span> octokit.rest.issues.createComment({
      owner,
      repo,
      <span class="hljs-attr">issue_number</span>: pr_number,
      <span class="hljs-attr">body</span>: <span class="hljs-string">`
        Pull Request #<span class="hljs-subst">${pr_number}</span> has been updated with: \n
        - <span class="hljs-subst">${diffData.changes}</span> changes \n
        - <span class="hljs-subst">${diffData.additions}</span> additions \n
        - <span class="hljs-subst">${diffData.deletions}</span> deletions \n
      `</span>
    });

  } <span class="hljs-keyword">catch</span> (error) {
    core.setFailed(error.message);
  }
}

<span class="hljs-comment">// Call the main function to run the action</span>
main();
</code></pre>
<h3 id="heading-6-push-our-action-files-to-github">6. Push our Action files to GitHub</h3>
<p>Let's stage, commit, and push our files to the main branch upstream:</p>
<pre><code class="lang-bash">$ git status
On branch main
Your branch is up to date with <span class="hljs-string">'origin/main'</span>.

Untracked files:
  (use <span class="hljs-string">"git add &lt;file&gt;..."</span> to include <span class="hljs-keyword">in</span> what will be committed)
 .gitignore
 action.yml
 index.js
 package-lock.json
 package.json

nothing added to commit but untracked files present (use <span class="hljs-string">"git add"</span> to track)
</code></pre>
<p>Let's add all the files to be staged:</p>
<pre><code class="lang-bash">$ git add .
</code></pre>
<p>Now we can commit our changes:</p>
<pre><code class="lang-bash">$ git commit -m <span class="hljs-string">"Add main action structure"</span>
[main 1fc5d18] Add main action structure
 5 files changed, 686 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 action.yml
 create mode 100644 index.js
 create mode 100644 package-lock.json
 create mode 100644 package.json
</code></pre>
<p>And push our changes:</p>
<pre><code class="lang-bash">$ git push origin main
Enumerating objects: 8, <span class="hljs-keyword">done</span>.
Counting objects: 100% (8/8), <span class="hljs-keyword">done</span>.
Delta compression using up to 16 threads
Compressing objects: 100% (7/7), <span class="hljs-keyword">done</span>.
Writing objects: 100% (7/7), 5.82 KiB | 5.82 MiB/s, <span class="hljs-keyword">done</span>.
Total 7 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:Link-/PR-metadata-action.git
   457fee2..1fc5d18  main -&gt; main
</code></pre>
<h3 id="heading-7-how-to-test-our-action">7. How to test our Action</h3>
<p>In order for us to be able to test our action, we need to create a bundle. If you noticed in the previous step, we did not push our <code>node_modules</code> folder which contains the packages we used in building our <code>index.js</code> file.</p>
<p>Our action will not run without those packages! To fix this we can use a nice tool called <a target="_blank" href="https://github.com/vercel/ncc">ncc</a>. It will help us create one file that includes our code and all the packages we need to run our action.</p>
<p>Let's start by installing <code>ncc</code>:</p>
<pre><code class="lang-bash">$ npm install @vercel/ncc

added 1 package, and audited 26 packages <span class="hljs-keyword">in</span> 5s

found 0 vulnerabilities
</code></pre>
<p>Compiling our JavaScript is as simple as running:</p>
<pre><code class="lang-bash">$ ncc build index.js -o dist
ncc: Version 0.22.1
ncc: Compiling file index.js
530kB  dist/index.js
530kB  [845ms] - ncc 0.22.1
</code></pre>
<p>This will create a new directory called <code>dist</code> and create a file called <code>index.js</code> that contains our code and all the packages we need to run our action.</p>
<p>Now we need to make sure our <code>action.yml</code> file contains the correct <code>runs</code> section. You need to replace:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">runs:</span>
  <span class="hljs-attr">using:</span> <span class="hljs-string">'node16'</span>
  <span class="hljs-attr">main:</span> <span class="hljs-string">'index.js'</span>
</code></pre>
<p>with:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">runs:</span>
  <span class="hljs-attr">using:</span> <span class="hljs-string">'node16'</span>
  <span class="hljs-attr">main:</span> <span class="hljs-string">'dist/index.js'</span>
</code></pre>
<p>Let's push our changes one more time upstream (to our GitHub repository). Make sure our <code>dist/</code> folder is not in the <code>.gitignore</code> file:</p>
<pre><code class="lang-bash">$ git status
$ git add .
$ git commit -m <span class="hljs-string">"Add compiled action"</span>
[main adfc4f0] Add compiled action
 4 files changed, 8505 insertions(+), 3 deletions(-)
 create mode 100644 dist/index.js
$ git push origin main
</code></pre>
<p>We're finally ready to create our workflow! Create a new workflow in the same or in any other repository (public or private doesn't matter) as follows:</p>
<pre><code class="lang-bash">mkdir -p .github/workflows
touch .github/workflows/pr-metadata.yaml
</code></pre>
<p>Copy the following workflow into our <code>pr-metadata.yaml</code> file:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">PR</span> <span class="hljs-string">metadata</span> <span class="hljs-string">annotation</span>

<span class="hljs-attr">on:</span> 
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">types:</span> [<span class="hljs-string">opened</span>, <span class="hljs-string">reopened</span>, <span class="hljs-string">synchronize</span>]

<span class="hljs-attr">jobs:</span>

  <span class="hljs-attr">annotate-pr:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Annotates</span> <span class="hljs-string">pull</span> <span class="hljs-string">request</span> <span class="hljs-string">with</span> <span class="hljs-string">metadata</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Annotate</span> <span class="hljs-string">PR</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">link-/PR-metadata-action@main</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">owner:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.repository_owner</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">repo:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.event.repository.name</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">pr_number:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.event.number</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
<p>When you're done with all these steps, our repository should look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/build-your-first-github-final_repo.png" alt="build-your-first-github-final_repo" width="600" height="400" loading="lazy"></p>
<p>In order for us to test this workflow, we need to make a change in our repository and create a Pull Request (PR). We can do this by editing the <code>README.md</code> file directly on GitHub:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/build-your-first-github_demo.gif" alt="build-your-first-github_demo" width="600" height="400" loading="lazy"></p>
<h2 id="heading-github-actions-best-practices">GitHub Actions Best Practices</h2>
<p>Lastly I want to share with you some best practices when creating custom Actions:</p>
<ul>
<li><p>Adopt the single responsibility principle. Make sure your actions <strong>do one thing only</strong>. It'll make your code easier to maintain and easier to test.</p>
</li>
<li><p>Think well about your action's interface (inputs and outputs). <strong>Keep your interfaces simple and clear by reducing the number of optional inputs.</strong></p>
</li>
<li><p>We didn't do it in this tutorial, but you need to <strong>validate your action's inputs!</strong> The majority of security projects could be eliminated by validating inputs.</p>
</li>
<li><p>Make sure your <strong>action is idempotent</strong>, meaning, if you run the action multiple times in a sequence the outcome should always be the same. In our case, the action should execute and post a comment and add the labels, or it should exit gracefully.</p>
</li>
<li><p>Read and <strong>follow the security hardening best practices</strong> documented in <a target="_blank" href="https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions">these GitHub Docs</a>.</p>
</li>
<li><p>Do not create a new action if you're not able to maintain it. <strong>Search for similar actions in the marketplace and use them instead</strong>.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>For this tutorial, we created a custom action that comments a summary of changes in a Pull Request and adds labels for the file types that were modified.</p>
<p>You should be able to reuse these steps to create more complex actions that can do so much more!</p>
<p>I'm in the process of creating a thorough DevOps course using GitHub Actions. If you're looking for more in-depth information on how you can use Actions for Continuous Integration, Continuous Delivery, or gitOps (among many other topics), keep an eye on these videos:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Ftq1yFwPJQ4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
