<?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: Michael Heap</title>
    <description>The latest articles on DEV Community by Michael Heap (@mheap).</description>
    <link>https://dev.to/mheap</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%2F240862%2F3dd58b5c-8058-4b36-8348-9e659ba5a4a5.jpeg</url>
      <title>DEV Community: Michael Heap</title>
      <link>https://dev.to/mheap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mheap"/>
    <language>en</language>
    <item>
      <title>The ultimate guide to GitHub Actions authentication</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Tue, 05 Oct 2021 16:01:58 +0000</pubDate>
      <link>https://dev.to/mheap/the-ultimate-guide-to-github-actions-authentication-1gco</link>
      <guid>https://dev.to/mheap/the-ultimate-guide-to-github-actions-authentication-1gco</guid>
      <description>&lt;p&gt;If you’ve done any work with GitHub Actions, you’ve probably come across the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; secret. This token allows you to interact with the GitHub API and push or pull a repositories contents. GitHub automatically generate a token for you, and it’s only available whilst your workflow jobs are running.&lt;/p&gt;

&lt;p&gt;This is awesome for 90% of the things you’d want to do with GitHub Actions, but what about the remaining 10%? The tasks where &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; doesn’t have the correct permissions, or when you want to trigger another GitHub Action somehow, but the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; key won’t let you?&lt;/p&gt;

&lt;p&gt;The good news is that you’re not out of luck! You’ve got two options for working with the GitHub API - a personal access token (or &lt;code&gt;PAT&lt;/code&gt;) and a GitHub App. A PAT is much simpler to set up, but a GitHub App is a lot more powerful. It’s entirely up to you which route you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before we move on to personal access tokens and GitHub Apps, I want to spend a little time talking about the magical &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; secret that GitHub provide. It works well enough for the majority of cases, so you probably haven’t thought too much about it.&lt;/p&gt;

&lt;p&gt;Even though it’s just &lt;em&gt;there&lt;/em&gt;, there’s a lot to &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Token Permissions
&lt;/h3&gt;

&lt;p&gt;I'm going to lead with this as it’s key but not many people know about it. &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; allows you to specify which permissions the token is granted.&lt;/p&gt;

&lt;p&gt;I’m going to say that one more time for the people in the back. &lt;strong&gt;&lt;code&gt;GITHUB_TOKEN&lt;/code&gt; allows you to specify which permissions the token is granted.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is &lt;em&gt;huge&lt;/em&gt;, as it means that a rogue action can only perform the actions that you’re expecting a workflow to do.&lt;/p&gt;

&lt;p&gt;Imagine that you work on a team where you use labels to mark pull requests as major, minor or patch version changes. If it’s a major change, you want to automatically add a comment explaining why the PR won’t be immediately merged. The workflow file you use would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;labeled&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;add-comment&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;evil-author/add-comment@main&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;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="na"&gt;comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thanks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PR!&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I've&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tagged&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;next&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;major&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;it&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;some&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;backwards&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;compatability&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;breaks"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;semver:major"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your workflow could work great for the first month, six months, or year. However, there’s no guarantees about what code &lt;code&gt;evil-author/add-comment@main&lt;/code&gt; will run at any point in time (if you &lt;strong&gt;do&lt;/strong&gt; want guarantees, check out &lt;a href="https://github.com/mheap/pin-github-action"&gt;pin-github-action&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Imagine that you’re running the action on a private repository full of sensitive information. Although you set out to automatically add comments to a pull request, somehow the &lt;code&gt;add-comment&lt;/code&gt; action has been compromised and the code on &lt;code&gt;main&lt;/code&gt; is doing something that you're not expecting. Now all your data has been sent off to a third party server by some code that wasn't there when you added the action to your workflow.&lt;/p&gt;

&lt;p&gt;You can prevent actions from doing anything except what you expect by using the &lt;code&gt;permissions&lt;/code&gt; key in your workflow file. If we want the &lt;code&gt;add-comment&lt;/code&gt; action to &lt;em&gt;only&lt;/em&gt; have the ability to add a comment to an issue, we can add the &lt;code&gt;issues&lt;/code&gt; permission:&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;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;labeled&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;issues&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;add-comment&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;evil-author/add-comment@main&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;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="na"&gt;comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thanks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PR!&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I've&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tagged&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;next&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;major&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;it&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;some&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;backwards&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;compatability&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;breaks"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;semver:major"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the &lt;code&gt;add-comment&lt;/code&gt; action is compromised with this workflow, it will be unable to read the repository’s contents using the API as you’ve only provided the &lt;code&gt;issues&lt;/code&gt; permission. When you set the &lt;code&gt;permissions&lt;/code&gt; key, all unspecified permissions default to no access.&lt;/p&gt;

&lt;p&gt;You can also provide &lt;code&gt;permissions&lt;/code&gt; at the &lt;code&gt;job&lt;/code&gt; level instead of, or in addition to, the workflow level. This allows you to provide specific permissions to actions that require them.&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;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;labeled&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;issues&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;add-comment&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;evil-author/add-comment@main&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;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="na"&gt;comment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Thanks&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PR!&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;I've&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tagged&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;this&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;next&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;major&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;release&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;it&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;has&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;some&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;backwards&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;compatability&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;breaks"&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;semver:major"&lt;/span&gt;
  &lt;span class="na"&gt;needs-checks&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;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;checks&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;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;trusted-user/update-check@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;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="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;merge-this-sprint&lt;/span&gt;
          &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;fail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be careful! If you don’t specify the &lt;code&gt;permissions&lt;/code&gt; key, the token will be granted read/write permission for all of the available scopes. If you’d like to change this behaviour, you can head in to your repository settings, choose the &lt;em&gt;Actions&lt;/em&gt; section on the left and then select &lt;em&gt;Read repository contents permission&lt;/em&gt; at the bottom.&lt;/p&gt;

&lt;p&gt;Once that’s done, any &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; generated in a workflow without a &lt;code&gt;permissions&lt;/code&gt; section will only be able to read repository contents and metadata - not pull requests, issues, checks or any of the other scopes.&lt;/p&gt;

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

&lt;p&gt;Be careful though, as even with this restricted configuration, the &lt;code&gt;read&lt;/code&gt; permission is granted for your repo contents (and the &lt;code&gt;contents&lt;/code&gt; permission covers &lt;a href="https://docs.github.com/en/rest/reference/permissions-required-for-github-apps#permission-on-contents"&gt;a lot more&lt;/a&gt; than just the files in the repo).&lt;/p&gt;

&lt;p&gt;If you’ve unsure which permissions your &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; has, you can see them listed in the &lt;em&gt;Set up job&lt;/em&gt; section of the logs for your run. It’ll look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt; GITHUB_TOKEN Permissions
  Actions: write
  Checks: write
  Contents: write
  Deployments: write
  Discussions: write
  Issues: write
  Metadata: read
  Packages: write
  PullRequests: write
  RepositoryProjects: write
  SecurityEvents: write
  Statuses: write
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;Staying with the security theme, there are a lot of security considerations around &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; in addition to being able to set granular permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expiration&lt;/strong&gt;: A new &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is generated when each job begins, and the token expires when the job is finished&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secure&lt;/strong&gt;: The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; secret will not be shown in logs and can not be extracted from the runner via HTTP (trust me, I’ve tried)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Available by default&lt;/strong&gt;: This is &lt;strong&gt;important&lt;/strong&gt;. GitHub Actions that you use can access the GitHub token &lt;em&gt;even if you don’t pass it in as an input&lt;/em&gt;. They can access it through the &lt;code&gt;github.token&lt;/code&gt; context, including setting it as a default input in their &lt;code&gt;action.yml&lt;/code&gt;. You should make sure that you set the minimum permissions required using the &lt;code&gt;permissions&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;: The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is scoped to the repository running the workflow. It cannot be used to make changes to any other repositories.&lt;/p&gt;

&lt;p&gt;These security considerations make the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; the best choice when interacting with the GitHub API in your actions unless you have specific requirements that require a behaviour that the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; can’t achieve.&lt;/p&gt;

&lt;h3&gt;
  
  
  When &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; isn’t enough
&lt;/h3&gt;

&lt;p&gt;The number 1 question that I’ve seen around the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is “&lt;em&gt;Why doesn’t it trigger new workflow runs?&lt;/em&gt;”. GitHub decided that any actions performed by the GitHub Actions bot should not trigger events, which means that you can’t use a workflow to trigger another workflow whilst using the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; secret. This is to prevent circular dependencies that can cause your builds to get stuck in a loop.&lt;/p&gt;

&lt;p&gt;The other question I frequently see is “&lt;em&gt;Can we change the user shown for &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;?&lt;/em&gt;”. This can be useful when changes are committed to a repository, or a comment is added to an issue or pull request. Rather than showing &lt;code&gt;github[bot]&lt;/code&gt; as the user, people would like to show a company branded account. This is possible when committing changes by setting the author email address, but not when adding an issue or pull request.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is limited to the current repository, which is great in most cases but prevents some workflows. If you want to make a change in another repository when a PR is merged, this is not possible with the default &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;. An example of this workflow is to automatically update a submodule pointer in repository B when &lt;code&gt;main&lt;/code&gt; changes in repository A.&lt;/p&gt;

&lt;p&gt;Fortunately there are ways to work around all of these limitations. Keep reading to understand the options and the pros and cons of each.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: It’s a GitHub App under the hood!
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; provided by actions isn’t something custom built especially for Actions. It leverages the existing GitHub Apps infrastructure, which is why the &lt;code&gt;permissions&lt;/code&gt; list is a lot closer to how GitHub Apps handles permissions than how user accounts handle scopes.&lt;/p&gt;

&lt;p&gt;Whilst the list of permissions is limited today, there may be a day when the full set of applications permissions is available to &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;. Imagine being able to control your Environments automatically using the provided &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I think GitHub have made the correct decision today, where there are limited permissions available as people learn about token security with Actions, but I’m interested to see what else they open up in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication options
&lt;/h2&gt;

&lt;p&gt;Now that we’ve covered &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; and the limitations you can face when using it, let’s take a look at the alternatives. Most of the workarounds you’ll find online recommend using a Personal Access Token (or PAT). There is another option though - GitHub Apps!&lt;/p&gt;

&lt;p&gt;In this section we’ll take a look at both personal access tokens and GitHub Apps to help decide which is the correct option for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personal Access Token (PAT)
&lt;/h2&gt;

&lt;p&gt;A personal access token (PAT) is an authentication token scoped to a single GitHub user account. The scopes available are the same as the scopes for &lt;a href="https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps"&gt;OAuth Apps&lt;/a&gt;. These tokens are typically used by an individual to work with their repositories and data, and as such the permissions are quite coarse. Granting the &lt;code&gt;repo&lt;/code&gt; scope allows access to almost everything&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Grants full access to repositories, including private repositories. That includes read/write access to code, commit statuses, repository and organization projects, invitations, collaborators, adding team memberships, deployment statuses, and repository webhooks for repositories and organizations. Also grants ability to manage user projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only areas that you can’t modify with a &lt;code&gt;repo&lt;/code&gt; scoped token are repo workflows and GitHub packages, in addition to some organization administration tasks.&lt;/p&gt;

&lt;p&gt;If you don’t need total access, there are sub-permissions available, such as &lt;code&gt;repo:status&lt;/code&gt;, &lt;code&gt;public_repo&lt;/code&gt; and &lt;code&gt;repo:invite&lt;/code&gt;. As always, selecting the minimum permissions required to achieve your task is key. If you don’t need access to private repositories, the &lt;code&gt;public_repo&lt;/code&gt; scope might be the correct one for you. Alternatively, if you only need to read and write commit statuses, the &lt;code&gt;repo:status&lt;/code&gt; permission may be all you need.&lt;/p&gt;

&lt;p&gt;If you’re going to use a PAT for automation purposes, I recommend creating a dedicated GitHub account to use. This allows you to control which repositories that account has access to, and prevents any integrations performing actions as your personal user. This is a common pattern, and even the GitHub docs team use it with their &lt;a href="https://github.com/octomerger"&gt;Octomerger Bot&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a PAT
&lt;/h3&gt;

&lt;p&gt;To create a personal access token, you can visit the &lt;a href="https://github.com/settings/tokens"&gt;PAT page&lt;/a&gt; in settings whilst logged in. This will show all of your existing tokens and allows you to create a new token.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure to add a good note for your new token. In 6 months time when you’re reviewing your tokens (you review regularly, right?) you’ll be thankful that you know why each token exists&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Token expiration is a recent feature, which I recommend you take advantage of. Having an expired token cause your workflow to fail isn’t ideal, but it’s better than having a leaked token be valid forever. My recommendation is to set this value as low as possible, with 30 days being my personal maximum validity. Rotating this credential is easy if you use organization secrets to define it once and provide access to multiple repositories. If you’re not using an org and need to update multiple repositories under a user account you may find &lt;a href="https://github.com/mheap/github-update-secret"&gt;github-update-secret&lt;/a&gt; useful.&lt;/p&gt;

&lt;p&gt;Finally, you have to choose your scopes. As mentioned above, try and minimise the scopes available. My personal workflow is to generate a token with &lt;code&gt;repo&lt;/code&gt; scope whilst developing a workflow, then revoke that token and build a new token with minimal permissions once the action is working. Unfortunately, there’s no way to provide the &lt;code&gt;issues&lt;/code&gt; permission without also allowing access to everything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;The biggest benefit to personal access tokens is how easy they are to get started with. You can switch from using &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; to a &lt;code&gt;PAT&lt;/code&gt; in under 5 minutes. Once you’ve switched, all the limitations of &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; disappear.&lt;/p&gt;

&lt;p&gt;You can now trigger a workflow from another workflow. I’ve used this to trigger automatic release generation whenever a pull request was merged to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also make changes across multiple repositories. Going back to an earlier example, you could now raise a pull request in repository B whenever &lt;code&gt;main&lt;/code&gt; changes in repository A.&lt;/p&gt;

&lt;p&gt;Finally, as your personal access token belongs to a user, any comments or commits added will show the username and profile image of your account. This can make the experience a little more personable, even though it’s still being automated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Downsides
&lt;/h3&gt;

&lt;p&gt;Although using a PAT makes it easy to get started, there are some downsides compared to using the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The biggest downside is that if your token is leaked, it can be used by people other than the GitHub Actions runner. If you set a long expiry time, this could go undetected for a while. If you set a short expiry time, you’ll spend all your time rotating credentials.&lt;/p&gt;

&lt;p&gt;The available permissions are also less granular than those offered by the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;. There is no way to provide access to just &lt;code&gt;issues&lt;/code&gt; without also giving access to everything else in the &lt;code&gt;repo&lt;/code&gt; scope.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is changing! If you're interested in granular scopes for personal access tokens, follow &lt;a href="https://github.com/github/roadmap/issues/184"&gt;this public roadmap item&lt;/a&gt; from GitHub&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Secondly, people join and leave companies. If your PAT is owned by someone that no longer works there, and no longer has access to your repos (with good reason!) all your workflows will stop working.&lt;/p&gt;

&lt;p&gt;If you use an automation account, it’s likely to be shared across multiple purposes on your team. This makes it more difficult to track who is making each change as these credentials can be used anywhere, not just within Actions.&lt;/p&gt;

&lt;p&gt;Finally, if you work at a company with GitHub enterprise enabled you may start hitting compliance issues. Shared accounts are frowned upon, and it gets even harder when your GitHub login is done through single sign on with your IDP. Suddenly you now have to pay for a few additional (or lots of additional, depending on how they feel about shared accounts) GitHub seats, each of which is mapped to an identity in your IDP. It &lt;em&gt;is&lt;/em&gt; possible (I’ve done it!), but the barrier to entry is much higher than the five minutes promised earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Apps
&lt;/h2&gt;

&lt;p&gt;If you don’t need to perform actions on behalf of a user, a GitHub Apps might be the right choice for you. Applications are how the majority of GitHub integrations are built, and is how people built custom workflows before Actions existed (usually using &lt;a href="https://probot.github.io/"&gt;Probot&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Applications excel when it comes to security. They give you the fine grained permissions that &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; provides, time limited tokens (without the need to rotate credentials) and more!&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an application
&lt;/h3&gt;

&lt;p&gt;Creating an application is a little more involved than creating a PAT. There are two types of application - GitHub Apps and OAuth apps. Make sure that you’re on the &lt;a href="https://github.com/settings/apps"&gt;GitHub Apps&lt;/a&gt; page in your settings. An application can be owned by an individual user account, or an organization. If you’re using this for work, I’d recommend creating the application under your org.&lt;/p&gt;

&lt;p&gt;The GitHub App name and description are usually critical when it comes to onboarding, but as this is intended to be an internal only application you can choose any name and description that you like. You’ll also need to provide a homepage URL, but this isn’t used by a callback workflow at all, so put in any URL you like.&lt;/p&gt;

&lt;p&gt;You don’t need to provide a Callback URL or Setup URL, and you’ll want to make sure that webhooks are deactivated.&lt;/p&gt;

&lt;p&gt;Finally, we’re on to &lt;em&gt;Repository permissions&lt;/em&gt;. This is the important part. Each of the permissions shown can be granted with a &lt;code&gt;none&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt; and in some cases, &lt;code&gt;admin&lt;/code&gt; permission. You can click on the &lt;code&gt;(i)&lt;/code&gt; next to each permission to learn more about which endpoints are covered by that permission. &lt;a href="https://docs.github.com/en/rest/reference/permissions-required-for-github-apps#permission-on-checks"&gt;Here&lt;/a&gt;is the list of endpoints accessible with the &lt;code&gt;checks&lt;/code&gt; permission. It can be quite hard to read, so let me break it down for you.&lt;/p&gt;

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

&lt;p&gt;If you grant the &lt;code&gt;checks&lt;/code&gt; permission to your application, you’ll be able to call &lt;code&gt;POST /repos/:owner/:repo/check-runs&lt;/code&gt; to create a check run, but only if you’ve granted &lt;code&gt;write&lt;/code&gt; permission. You’ll also be able to call &lt;code&gt;GET /repos/:owner/:repo/check-runs/:check_run_id&lt;/code&gt; if you have &lt;code&gt;read&lt;/code&gt; &lt;em&gt;or&lt;/em&gt; &lt;code&gt;write&lt;/code&gt; permission.&lt;/p&gt;

&lt;p&gt;It can be hard to match up each endpoint to what you’re trying to achieve, but you can click each request to learn more about each endpoint’s capabilities and parameters.&lt;/p&gt;

&lt;p&gt;It’s important to note that the permissions you select when creating an application are the &lt;strong&gt;maximum permissions available to that application&lt;/strong&gt;. You can request fewer permissions in the generated token, but you can’t ask for extra permissions. With that in mind, I’d spend some time thinking about what your actions need to achieve and set these permissions appropriately.&lt;/p&gt;

&lt;p&gt;Right at the bottom there’s a &lt;em&gt;User permissions&lt;/em&gt; section - we can ignore this as we’ll be making server to server requests rather than editing users.&lt;/p&gt;

&lt;p&gt;Finally, you need to decide where the application can be installed. I tend to allow it only on my account and create new applications in each organization that needs to use application authentication.&lt;/p&gt;

&lt;p&gt;Create your application and save your &lt;code&gt;App ID&lt;/code&gt; and &lt;code&gt;Private Key&lt;/code&gt; in a safe place as you’ll need them both in the next step. You’ll also need to &lt;a href="https://docs.github.com/en/developers/apps/managing-github-apps/installing-github-apps#installing-your-private-github-app-on-your-repository"&gt;install it on your account or organization&lt;/a&gt; before using it to create authentication tokens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using applications in a GitHub workflow
&lt;/h3&gt;

&lt;p&gt;Now that you’ve created an application, it’s time to update your workflows to generate an authentication token using that app rather than using &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; or a &lt;code&gt;PAT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are a couple of actions available, but I like &lt;a href="https://github.com/peter-murray/workflow-application-token-action"&gt;this one from Peter Murray&lt;/a&gt; as it works for applications installed at both an organization and a repository level.&lt;/p&gt;

&lt;p&gt;To use this action you’ll need to create two secrets in your repo or organization: &lt;code&gt;APPLICATION_ID&lt;/code&gt; and &lt;code&gt;APPLICATION_PRIVATE_KEY&lt;/code&gt; using the details you saved when creating an application.&lt;/p&gt;

&lt;p&gt;Once that’s done, it’s time to update your workflow! Imagine that you have the following workflow that creates a release using the provided &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create-release&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use Application Token to create a release&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/create-release@v1&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="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but you’d like to create a release as your company’s application rather than the GitHub bot. By adding an additional step to generate a token using your application, you can use the token output in the &lt;code&gt;create-release&lt;/code&gt; action:&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;create-release&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get Token&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get_workflow_token&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;peter-murray/workflow-application-token-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.APPLICATION_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;application_private_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.APPLICATION_PRIVATE_KEY }}&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;Use Application Token to create a release&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/create-release@v1&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;${{ steps.get_workflow_token.outputs.token }}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the release with your application’s identity, rather than using the GitHub bot.&lt;/p&gt;

&lt;p&gt;I mentioned earlier that you can request limited permissions from an application. The example above will return a token that has the maximum permissions available to an application. Here’s an example of a workflow that requests &lt;code&gt;write&lt;/code&gt; access to &lt;code&gt;issues&lt;/code&gt; and &lt;code&gt;read&lt;/code&gt; access to &lt;code&gt;deployments&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Support for permissions is a recent addition and is not available until &lt;a href="https://github.com/peter-murray/workflow-application-token-action/pull/6"&gt;this PR&lt;/a&gt; is merged&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;permission-demo&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get Token&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get_workflow_token&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;peter-murray/workflow-application-token-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.APPLICATION_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;application_private_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.APPLICATION_PRIVATE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;issues:write,deployments:read"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ve now got a solution that has all the benefits of &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; such as granular permissions and automatic expiry, but none of the downsides. Let’s dig in to the benefits and downsides of using a GitHub App for authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;p&gt;Let’s go back to the number 1 question that people have about &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;. “&lt;em&gt;Why does my workflow not trigger other workflows?&lt;/em&gt;”. Using a GitHub App solves this problem, just like using a personal access token does. However, there are other benefits that applications bring over a PAT.&lt;/p&gt;

&lt;p&gt;Application tokens are valid for a very short amount of time. Using this action, the token is valid for 60 seconds from the moment it’s created. This means that even if it’s leaked by an action, it will be useless almost immediately.&lt;/p&gt;

&lt;p&gt;As this is an application and not an account, there’s no shared account for people to log in to. This means better accountability, and no SSO/IDP issues mentioned above if you’re using GitHub Enterprise.&lt;/p&gt;

&lt;p&gt;Finally, the biggest benefit to using applications is how granular the permissions are. Here are all the different permissions that you can allow using applications:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository permissions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actions&lt;/li&gt;
&lt;li&gt;Administration&lt;/li&gt;
&lt;li&gt;Checks&lt;/li&gt;
&lt;li&gt;Content references&lt;/li&gt;
&lt;li&gt;Contents&lt;/li&gt;
&lt;li&gt;Deployments&lt;/li&gt;
&lt;li&gt;Discussions&lt;/li&gt;
&lt;li&gt;Environments&lt;/li&gt;
&lt;li&gt;Issues&lt;/li&gt;
&lt;li&gt;Metadata&lt;/li&gt;
&lt;li&gt;Organization packages&lt;/li&gt;
&lt;li&gt;Packages&lt;/li&gt;
&lt;li&gt;Pages&lt;/li&gt;
&lt;li&gt;Pull requests&lt;/li&gt;
&lt;li&gt;Webhooks&lt;/li&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;li&gt;Secret scanning alerts&lt;/li&gt;
&lt;li&gt;Secrets&lt;/li&gt;
&lt;li&gt;Security events&lt;/li&gt;
&lt;li&gt;Single file&lt;/li&gt;
&lt;li&gt;Commit statuses&lt;/li&gt;
&lt;li&gt;Dependabot alerts&lt;/li&gt;
&lt;li&gt;Workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Organization permissions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Members&lt;/li&gt;
&lt;li&gt;Administration&lt;/li&gt;
&lt;li&gt;Events&lt;/li&gt;
&lt;li&gt;Webhooks&lt;/li&gt;
&lt;li&gt;Plan&lt;/li&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;li&gt;Secrets&lt;/li&gt;
&lt;li&gt;Self-hosted runners&lt;/li&gt;
&lt;li&gt;Blocking users&lt;/li&gt;
&lt;li&gt;Team discussions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Downsides
&lt;/h3&gt;

&lt;p&gt;The biggest downside to using a GitHub App is that you need to edit every workflow to add an additional step where you generate a token for use later on. This can make your workflows harder to read for those that aren’t familiar with the application token workflow.&lt;/p&gt;

&lt;p&gt;If you need to perform any actions as a user, this authentication mechanism won’t work for you. Whilst GitHub App &lt;em&gt;do&lt;/em&gt; support user OAuth, the way we’ve configured it for use with Actions they’re not suitable for managing user profiles.&lt;/p&gt;

&lt;p&gt;Finally, understanding all of the available permissions can be tough. Using applications for authentication has a lot of benefits even if you don’t use it to scope permissions to the minimum required, but you’re losing the biggest benefit if you don’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  So which is the best?
&lt;/h2&gt;

&lt;p&gt;There are three options for authentication when it comes to GitHub Actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;GITHUB_TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use a Personal Access Token (PAT)&lt;/li&gt;
&lt;li&gt;Generate credentials with a GitHub App&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each of these options have pros and cons. If you can achieve everything you need using &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;, I’d stick with that for your workflows. It’s well understood by the community and reduces the number of moving parts in your workflows.&lt;/p&gt;

&lt;p&gt;Given the choice between a PAT and a GitHub App, I’d choose a GitHub App &lt;em&gt;unless&lt;/em&gt; you need to perform actions as a user. It solves the main problems with &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; (triggering new workflow runs, posting as an identity other than &lt;code&gt;github[bot]&lt;/code&gt;) without any of the issues that a &lt;code&gt;PAT&lt;/code&gt; introduces (long lived tokens, shared accounts).&lt;/p&gt;

&lt;p&gt;That concludes the ultimate guide to GitHub Actions authentication. If you’ve got any questions or comments, I’m &lt;a href="https://twitter.com/mheap/"&gt;@mheap on Twitter&lt;/a&gt; and I’d love to hear them.&lt;/p&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
      <category>security</category>
    </item>
    <item>
      <title>8 Common Request Transformation Policies</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Tue, 27 Jul 2021 13:00:17 +0000</pubDate>
      <link>https://dev.to/kong/8-common-request-transformation-policies-2fp6</link>
      <guid>https://dev.to/kong/8-common-request-transformation-policies-2fp6</guid>
      <description>&lt;p&gt;API gateway request transformation policies are incredibly powerful. There are many situations when an API developer can take advantage of request transformations to adjust the shape and values of a request to cleanly fit their API.&lt;/p&gt;

&lt;p&gt;Let’s say you’re deprecating a certain endpoint for your API, but you still need to support the old specification for a transition period. Request transformations let you accept requests according to the old specification, transform them to fit the new specification and then forward them.&lt;/p&gt;

&lt;p&gt;Or maybe you discovered that incorrect documentation on an endpoint’s query parameter specification has been in the wild for far too long, which is why so many users have been encountering errors. Rather than changing your API to fit bad documentation, you can use request transformations to modify requests as they come in, shaping them according to how your API is supposed to work.&lt;/p&gt;

&lt;p&gt;Or perhaps it’s simply time to implement basic security best practices, like stripping or obfuscating sensitive data in the headers before letting them continue upstream.&lt;/p&gt;

&lt;p&gt;For these and many other reasons, request transformations can be your go-to solution.&lt;/p&gt;

&lt;p&gt;This article will look at how we might use &lt;a href="https://konghq.com/kong/"&gt;Kong Gateway&lt;/a&gt;, coupled with the Request Transformer &lt;a href="https://docs.konghq.com/hub/kong-inc/request-transformer/"&gt;plugin&lt;/a&gt;, to configure, intercept and transform requests as they make their way to an upstream route. Setup and configuration are incredibly simple. We’ll cover several common use cases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copying an API Key from the query string to the header&lt;/li&gt;
&lt;li&gt;Removing a query string value&lt;/li&gt;
&lt;li&gt;Moving an API key from a query string to the header&lt;/li&gt;
&lt;li&gt;Adding a version number to a query string&lt;/li&gt;
&lt;li&gt;Modifying the header token to Bearer Auth&lt;/li&gt;
&lt;li&gt;Moving JWT from header to body&lt;/li&gt;
&lt;li&gt;Sanitizing the body&lt;/li&gt;
&lt;li&gt;Changing the HTTP method&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This should give you enough foundation to craft your own transformations, custom-tailored for your application situation.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Overview of Core Concepts&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Before we dive in, let’s quickly cover the technologies we’ll be using for our walkthrough.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Kong Gateway&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Kong Gateway is a thin-layer, front-line “gateway” sitting in front of your system’s upstream services. Whether those upstream services are API servers, web servers or any other cloud microservices, Kong Gateway handles traffic control, authentication, load balancing and more. &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Request Transformer Plugin&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The Request Transformer plugin for Kong Gateway comes built in. As requests come through Kong, you can configure the plugin to transform those requests—mutating headers, query string parameters, the request body and so on—before forwarding those requests to their final destination. Transformations are highly configurable and extremely powerful, giving you a lot of control over the precise shape of requests before they hit your endpoint.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Mockbin&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://mockbin.org/"&gt;Mockbin&lt;/a&gt; is an online custom endpoint generator used for testing HTTP requests and tracking responses. Mockbin is exactly what we need for our walkthrough—as we set up transformations, send requests and then inspect the result.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Our Walkthrough Approach&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To start, we’ll set up our Mockbin endpoint as our “upstream service.” We only need a single endpoint, and we’ll hit it with &lt;code&gt;GET&lt;/code&gt; and &lt;code&gt;POST&lt;/code&gt; requests. Then, we’ll make sure Kong Gateway is installed and configured properly.&lt;/p&gt;

&lt;p&gt;We’ll look at configuring the Request Transformer plugin for each transformation that we want to demonstrate. Then, we’ll send our request (using &lt;code&gt;curl&lt;/code&gt;). Finally, we’ll inspect the request at Mockbin. We want to see what the request looked like when it arrived at Mockbin &lt;em&gt;after&lt;/em&gt; passing through Kong and the Request Transformer plugin.&lt;/p&gt;

&lt;p&gt;Are you ready? Roll out!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to set up a request transformation for your API gateway with clicks instead of code?&lt;/strong&gt; &lt;a href="https://konghq.com/kong-konnect/"&gt;&lt;strong&gt;Try Konnect for free &amp;gt;&amp;gt;&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Set Up Mockbin Endpoint&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Mockbin is a simple and easy-to-use test endpoint generator. On the website, click on the &lt;strong&gt;Create Bin&lt;/strong&gt; link to get started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hmpZxh_8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-website.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hmpZxh_8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-website.png" alt="mockbin-website"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zUss9mZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-create-bin.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zUss9mZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-create-bin.png" alt="mockbin-create-bin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, just like that, your endpoint has been created! From here, you will need your endpoint URL. This is just &lt;a href="https://mockbin.org/bin/%7BBIN-IDENTIFIER%7D"&gt;https://mockbin.org/bin/{BIN-IDENTIFIER}&lt;/a&gt;. But, for good measure, you can right-click on &lt;strong&gt;Visit in Browser&lt;/strong&gt; and copy the link address. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LELaE-yN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-get-bin-link.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LELaE-yN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-get-bin-link.png" alt="mockbin-get-bin-link"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test it out, you can open a new tab in your browser and visit the link you just copied. Doing so will send a &lt;code&gt;GET&lt;/code&gt; request to your Mockbin endpoint. To see the details for this request, go back to the browser tab with your Mockbin details and click on &lt;strong&gt;View History&lt;/strong&gt;. You’ll see a running log of requests to this endpoint. You should see the &lt;code&gt;GET&lt;/code&gt; request that you just sent:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gwAwxUk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-history.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gwAwxUk7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/mockbin-history.png" alt="mockbin-history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Mockbin endpoint is up and running and ready to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Set Up Kong Gateway&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Spinning up Kong Gateway is quite straightforward. First, you’ll need to &lt;a href="https://konghq.com/install/"&gt;install Kong&lt;/a&gt; on your local machine. There are many different installation options, so you can choose whichever best fits your environment.&lt;/p&gt;

&lt;p&gt;After installing Kong, create a project folder on your local machine. For simplicity, I’m creating a sub-folder called “project” in my home folder. Then, in that folder, run the command to generate a declarative configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~$ mkdir project
~$ cd project
~/project$ kong config init
~/project$ tree
.
└── kong.yml

0 directories, 1 file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For our use of the Request Transformer plugin, we can put all of our service and route setup and plugin configurations in a single file. Then, start Kong. Kong does not need to write to a database, and we don’t need to do any further on-the-fly configuration as we go along. This is Kong’s &lt;a href="https://docs.konghq.com/gateway-oss/2.4.x/db-less-and-declarative-config/"&gt;DB-less and Declarative Configuration&lt;/a&gt;, and it’s sufficient for what we need.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;kong.yml&lt;/code&gt; file is a starter template for a declarative configuration file. Let’s open it and modify it to look like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: untouched
  service: mockbin
  paths:
    - /untouched
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s briefly go over what we’ve done here. The &lt;code&gt;&lt;br&gt;
_format_version&lt;/code&gt; line, which is required, specifies the version number of the declarative configuration syntax we’re using. Next, we declare an upstream service, and we give it the arbitrary name “mockbin” and specify the URL for this upstream service. Make sure you use your unique URL for the Mockbin endpoint you created earlier.&lt;/p&gt;

&lt;p&gt;Next, we create a route, which we will arbitrarily name “untouched.” This route listens on the Kong Gateway for requests that go to the path &lt;code&gt;/untouched&lt;/code&gt;, and then it forwards those requests to our upstream service called “mockbin.” In this first example, we are not adding a Request Transformer plugin. Requests will proceed through Kong Gateway untouched, continuing to Mockbin.&lt;/p&gt;

&lt;p&gt;We’re almost ready! The last thing we need to do is tell Kong where to look for our declarative configuration file when it starts up. To do this, we need a &lt;code&gt;kong.conf&lt;/code&gt; file, copied from the default template provided to us upon installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ cd /etc/kong
/etc/kong$ cp kong.conf.default kong.conf
~/project$ sudo vi kong.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;kong.conf&lt;/code&gt; file, there are two lines that we need to edit. At around line 922, we need to tell Kong that we won’t be using a database. And at around line 1106, we need to provide the absolute path to our declarative configuration file. That’s the &lt;code&gt;kong.conf&lt;/code&gt; file in your project folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PATH: /etc/kong/kong.conf

...

# AROUND LINE 922
# inherited from the corresponding main connection config described above but
# may be optionally overwritten explicitly using the `pg_ro_*` config below.

database = off # Determines which of PostgreSQL or Cassandra
                              # this node will use as its datastore.

# AROUND LINE 1106
                              # This value is only used during
                              # migrations.

declarative_config = /PATH/TO/YOUR/PROJECT/FOLDER/kong.yml
                              # The path to the declarative configuration
                              # file which holds the specification of all
                              # entities (Routes, Services, Consumers, etc.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the file saved, we’re ready to start up Kong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that Kong is running, let’s send a request to &lt;code&gt;localhost:8000&lt;/code&gt;, which is the port where Kong is listening. We’ll send a &lt;code&gt;GET&lt;/code&gt; request to the &lt;code&gt;/untouched&lt;/code&gt; endpoint, and we’ll tack on a query string parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ curl -X GET 'http://localhost:8000/untouched?hello=world'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your browser, refresh the page with your Mockbin endpoint history log. You should see a new entry for a &lt;code&gt;GET&lt;/code&gt; request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sDWNiMOM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_GET-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sDWNiMOM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_GET-01.png" alt="untouched_GET-01"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you view the Request Details, you can see that the &lt;code&gt;queryString&lt;/code&gt; array has a pair that matches the parameters we sent to Kong. It looks like Kong successfully forwarded our (untouched) request to Mockbin!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LdP2tI-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_GET-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LdP2tI-I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_GET-02.png" alt="untouched_GET-02"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just for good measure, let’s also send a &lt;code&gt;POST&lt;/code&gt; request to the same endpoint at Kong. We’ll set our “Content-Type” header and tack on a request body like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ curl -X POST \
           -H 'Content-Type: application/json' \
           -d '{"hello":"world"}' \
           'http://localhost:8000/untouched'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, we refresh the Mockbin log, and we see our &lt;code&gt;POST&lt;/code&gt; request. Here, we see our Request Body, which matches what we sent to Kong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--brDdcHh---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_POST_01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--brDdcHh---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_POST_01.png" alt="untouched_POST_01"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we poke around at the Request Details, we also see the “Content-Type” value that we set in our header, along with our &lt;code&gt;POST&lt;/code&gt; body data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3x5t2Rbp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/unnamed-1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3x5t2Rbp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/unnamed-1.png" alt="untouched_POST_02"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZKzz-3xx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_POST_03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZKzz-3xx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/untouched_POST_03.png" alt="untouched_POST_03"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like we have finished our setup. We’ve connected all the pieces. It’s time to start playing around with some common request transformations.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Common Request Transformations&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Copy API key from query string to header&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s start with a common scenario. Your API users have been adding their API key as a query string parameter, but you’d like to transition them toward attaching it as a custom header value instead. Until you’re certain that all of your API users have gotten the memo that the key should be in the header, you want to accommodate the stragglers. What you need is a request transformation that copies the value from the query string parameter to a new header value.&lt;/p&gt;

&lt;p&gt;Here is how our &lt;code&gt;kong.yml&lt;/code&gt; file would look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: copy-query-to-header
  service: mockbin
  paths:
    - /copy-q2h
plugins:
- name: request-transformer
  route: copy-query-to-header
  config:
    add:
      headers:
        - api-key:$(query_params.api_key)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to our first example, we’ve created a service, along with a route that forwards to this service. For this route, Kong will listen on the path &lt;code&gt;/copy-q2h&lt;/code&gt;. Next, we add a plugin. The name of this plugin ( &lt;strong&gt;not&lt;/strong&gt; arbitrary) is &lt;code&gt;request-transformer&lt;/code&gt;. The plugin is for our specific route.&lt;/p&gt;

&lt;p&gt;What kinds of request transformations should this plugin do? It will add a header. The new key for this header will be &lt;code&gt;api-key&lt;/code&gt;. The value will be copied from the query string parameter called &lt;code&gt;api_key&lt;/code&gt;. This part uses the &lt;a href="https://docs.konghq.com/hub/kong-inc/request-transformer/#template-as-value"&gt;plugin’s templating feature&lt;/a&gt; to grab a value that came in the query string&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don’t forget: Whenever we modify the kong.yml file, we need to restart Kong.&lt;/strong&gt; Let’s restart Kong and send our request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X GET 'http://localhost:8000/copy-q2h?api_key=THISISMYAPIKEY'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve sent a &lt;code&gt;GET&lt;/code&gt; request to our &lt;code&gt;/copy-q2h&lt;/code&gt; endpoint. We tacked on a query string parameter &lt;code&gt;api_key=THISISMYAPIKEY&lt;/code&gt;. Let’s see how this request was transformed by refreshing the Mockbin log.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rxa-ahnQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/copy-q2h-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rxa-ahnQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/copy-q2h-01.png" alt="copy-q2h-01"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we scroll down the Request Details, we see in the headers that there is an &lt;code&gt;api-key&lt;/code&gt; header with the value &lt;code&gt;THISISMYAPIKEY&lt;/code&gt;. It looks like our copy transformation worked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wL9kse1Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/copy-q2h-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wL9kse1Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/copy-q2h-02.png" alt="copy-q2h-02"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will notice that the &lt;code&gt;api_key=THISISMYAPIKEY&lt;/code&gt; query string parameter is still in the request, though. That’s okay for now. We’ll deal with that in a little bit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AtPbx_WZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/copy-q2h-03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AtPbx_WZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/copy-q2h-03.png" alt="copy-q2h-03"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Remove query string value&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Another common scenario involves the need to remove certain query string parameters completely. Perhaps for privacy reasons or security reasons, sanitizing query parameters is a common use case for request transformations.&lt;/p&gt;

&lt;p&gt;In the following &lt;code&gt;kong.yml&lt;/code&gt; example, we want to remove the key-value pairs for the &lt;code&gt;api_key&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; query string parameters. Similar to our previous example, we create a route (listening on &lt;code&gt;/strip-q&lt;/code&gt;), and we create a plugin on that route that removes our undesirable parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: strip-query
  service: mockbin
  paths:
    - /strip-q
plugins:
- name: request-transformer
  route: strip-query
  config:
    remove:
      querystring:
        - api_key
        - password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s send a request to this path, adding on a few query parameters. We’ll include the ones we want to strip away, along with one that we would like to keep. Don’t forget to restart Kong!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X GET \
'http://localhost:8000/strip-q?api_key=THISISMYAPIKEY&amp;amp;username=johndoe&amp;amp;password=THISISMYPASSWORD'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Taking a glance at the Mockbin log, we can see that this most recent GET request received the query string parameter we wanted to keep (&lt;code&gt;username=johndoe&lt;/code&gt;) but didn’t receive the undesirable ones. That’s a good sign.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--59HjX3Dt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/strip-q-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--59HjX3Dt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/strip-q-01.png" alt="strip-q-01"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A closer look at the query string array in Request Details shows that the plugin worked as expected, removing the &lt;code&gt;api_key&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; parameters before forwarding the request to Mockbin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3ur_4ilm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/Remove-Query-String-Value-Password.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3ur_4ilm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/Remove-Query-String-Value-Password.png" alt="Remove Query String Value Password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Just a note:&lt;/strong&gt; You could write &lt;code&gt;kong.yml&lt;/code&gt; with multiple routes and multiple plugins. In this article, our file always shows just a single route and a single plugin. That’s just to keep it simple, but you’re not restricted to do it this way.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Move API key from query string to header&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Previously, we copied an API key from the query parameter to a header value, but we left the query parameter in the request. In the following example, let’s do something similar, but we’ll clean up after ourselves by removing the query parameter too. Our configuration for the plugin this time around: 1) adds a header based on the query parameter value, and then 2) removes the query parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: move-query-to-header
  service: mockbin
  paths:
    - /move-q2h
plugins:
- name: request-transformer
  route: move-query-to-header
  config:
    add:
      headers:
        - api-key:$(query_params.api_key)
    remove:
      querystring:
        - api_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the keen observer might be asking: “&lt;em&gt;Wait, if you remove the query parameter, how would you copy its value over to the header? Does the configuration order matter, where you have to copy it before you remove it?&lt;/em&gt;” The answer is… no! Kong’s documentation on template values explains it this way:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The plugin creates a non-mutable table of request headers, query strings, and captured URIs before the transformation. Therefore, any update or removal of params used in a template does not affect the rendered value of a template.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s restart Kong and send our request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X GET 'http://localhost:8000/move-q2h?api_key=THISISMYAPIKEY'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An inspection of the Request Details at the Mockbin log shows the value in our headers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HRzV02KT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/API-Key-Parameters.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HRzV02KT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/API-Key-Parameters.png" alt="API Key Parameters"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Add version number to query string&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Another common case is simply adding a query string parameter to a request. In this example, we’ll add a fixed API version number to all requests that come through our &lt;code&gt;/add-q&lt;/code&gt; route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: add-to-query
  service: mockbin
  paths:
    - /add-q
plugins:
- name: request-transformer
  route: add-to-query
  config:
    add:
      querystring:
        - api_version:2.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will add the parameter called &lt;code&gt;api_version&lt;/code&gt;, and its value will be &lt;code&gt;2.0&lt;/code&gt;. Let’s restart Kong and send our request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X GET 'http://localhost:8000/add-q?username=johndoe'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While we only set one query parameter (&lt;code&gt;username=johndoe&lt;/code&gt;), we inspect our transformed request at the Mockbin log. There we see in the Request Details that Mockbin received two parameters with the request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HfWsTk-R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/add-q-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HfWsTk-R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/add-q-02.png" alt="add-q-02"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like our query parameter add was successful.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. Modify header token to Bearer Auth&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Now, let’s experiment with our headers a bit. Imagine the scenario where your API users are attaching their authorization token as a header called &lt;code&gt;token&lt;/code&gt;, but you wrote your API to use the Bearer Auth scheme. The token value should be in a header called &lt;code&gt;Authorization&lt;/code&gt;, following the word “Bearer!” Collin, the junior dev who wrote that erroneous API documentation, will get a poor performance review. In the meantime, you’ll use request transformation to fix Collin’s mess.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: modify-header
  service: mockbin
  paths:
    - /token-to-auth
plugins:
- name: request-transformer
  route: modify-header
  config:
    add:
      headers:
        - Authorization:Bearer $(headers["token"])
    remove:
      headers:
        - token
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our configuration again takes advantage of template values, copying the &lt;code&gt;token&lt;/code&gt; value from the token header and structuring a proper Bearer Auth header value. And, of course, we remove the undesirable &lt;code&gt;token&lt;/code&gt; header after we get what we need.&lt;/p&gt;

&lt;p&gt;We restart Kong and send our request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X GET \
-H 'token:thisisanopaquestringthatrepresentsajwt' \
'http://localhost:8000/token-to-auth'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this request, we sent our token value in the header called &lt;br&gt;
&lt;code&gt;token&lt;/code&gt;. Let’s inspect the Request Details at our Mockbin log:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BGjBQMeu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/token-to-auth-02.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BGjBQMeu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/token-to-auth-02.png" alt="token-to-auth-02"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We see the Bearer Auth scheme in our header now. A further inspection of the details shows that the offending &lt;code&gt;token&lt;/code&gt; header is not present. On a side note, you might notice above that we had configured our plugin to add a header with the name “Authorization” (capitalized), but what shows up in Mockbin is “authorized” (lowercase). It looks like this might be an issue with Mockbin—which lowercases all of its header names—and not an issue with the Request Transformer plugin (which addressed this specific issue in a &lt;a href="https://github.com/Kong/kong-plugin-request-transformer/pull/34"&gt;pull request&lt;/a&gt;).&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;6. Move JWT from header (Bearer Auth) to body&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s do some request transformations that modify the request body. In this example, we’ll take the token in Bearer Auth format, and we’ll write it to our request JSON body as a value. While some upstream services look in the header for an authorization token, others might look to the request body. Request transformations provide flexibility, especially when modifying upstream services isn’t an option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: jwt-to-body
  service: mockbin
  paths:
    - /jwt2b
plugins:
- name: request-transformer
  route: jwt-to-body
  config:
    add:
      body:
        - jwt:$(headers["Authorization"]:gsub("^Bearer ",""))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The syntax for the above plugin configuration is a bit more complicated, but you can probably discern what’s happening. We want to add a key-value pair to our request body. The key is &lt;code&gt;jwt&lt;/code&gt;, and the value will be taken with the &lt;code&gt;Authorization&lt;/code&gt; header but with the initial “Bearer” (plus space) removed.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.konghq.com/hub/kong-inc/request-transformer/#advanced-templates"&gt;plugin documentation&lt;/a&gt; notes that the placeholder is evaluated as Lua expression. So, if you know &lt;a href="https://www.lua.org/manual/5.4/"&gt;Lua&lt;/a&gt;, you can probably do some pretty powerful transformations.&lt;/p&gt;

&lt;p&gt;Let’s restart Kong and send our &lt;code&gt;POST&lt;/code&gt; request, along with headers and body data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer thisisanopaquestringthatrepresentsajwt' \
-d '{"username":"johndoe" }' \
'http://localhost:8000/jwt2b'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we look at our Mockbin log for this request, we immediately see the Request Body. It contains the original body we sent (&lt;code&gt;"username":"johndoe"&lt;/code&gt;), but it also contains &lt;br&gt;
&lt;code&gt;jwt&lt;/code&gt;, with the token value that was in our Bearer Auth header.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lCTKtQ0r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/jwt-to-body-01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lCTKtQ0r--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/jwt-to-body-01.png" alt="jwt-to-body-01"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might notice, from our plugin configuration, that we decided to leave our Bearer Auth header in the request:&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;7. Sanitize body&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Perhaps for sensitive data that might come through the request body, you will need a request transformation that sanitizes it but doesn’t remove it. This helps your upstream service know that the data &lt;em&gt;did&lt;/em&gt; come through, but it’s just no longer available. For this type of transformation, your configuration might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: sanitize-body
  service: mockbin
  paths:
    - /san-b
plugins:
- name: request-transformer
  route: sanitize-body
  config:
    replace:
      body:
      - last4: ****
      - api_key: ********
      - password: ********
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this request transformation, we’re looking in our JSON body for &lt;code&gt;last4&lt;/code&gt;, &lt;code&gt;api_key&lt;/code&gt; or &lt;code&gt;password&lt;/code&gt;. For any of those keys, we want to replace the provided value with a redacted one. After restarting Kong, we send our request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X POST \
-H 'Content-Type: application/json' \
-d '{"api_key":"THISISMYAPIKEY", "username":"johndoe", "password":"THISISMYPASSWORD", "last4":9876}' \
'http://localhost:8000/san-b'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick glance at the Mockbin log shows our transformed Request Body. Recall that Kong transformed this body &lt;em&gt;before&lt;/em&gt; it was sent to the upstream service, so we can be sure that none of this sensitive data got past our gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;8. Change HTTP method&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For our final example, let’s demonstrate a request method transformation. Let’s assume that &lt;code&gt;PUT&lt;/code&gt; requests to a given route need to be converted to &lt;code&gt;POST&lt;/code&gt; requests, and instead, we’d add an &lt;code&gt;"action":"PUT"&lt;/code&gt; key-value pair into the JSON request body.&lt;/p&gt;

&lt;p&gt;In this configuration, we configure our route only to listen for &lt;code&gt;PUT&lt;/code&gt; requests. Then, we configure the plugin to transform the &lt;code&gt;http_method&lt;/code&gt; to &lt;code&gt;POST&lt;/code&gt;. Lastly, we add a key-value pair to our body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/project/kong.yml
_format_version: "2.1"

services:
- name: mockbin
  url: https://mockbin.org/bin/REPLACE-WITH-YOUR-BIN-IDENTIFIER
routes:
- name: put-to-post
  service: mockbin
  methods:
    - PUT
  paths:
    - /put-to-post
plugins:
- name: request-transformer
  route: put-to-post
  config:
    http_method: POST
    add:
      body:
      - action:PUT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Kong and send the &lt;code&gt;PUT&lt;/code&gt; request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
~/project$ curl -X PUT \
-H 'Content-Type: application/json' \
-d '{"username":"johndoe" }' \
'http://localhost:8000/put-to-post'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We see the successful result in our Mockbin log. The most recent request was a &lt;code&gt;POST&lt;/code&gt; (not &lt;code&gt;PUT&lt;/code&gt;) request. And, we see the &lt;code&gt;action&lt;/code&gt; key-value pair added to our Request Body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XW7TTs4D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/unnamed-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XW7TTs4D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/uploads/2021/07/unnamed-3.png" alt="Successful Request Transformation Mockbin"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We’ve covered a lot of examples in this walkthrough. We’ve transformed query string parameters, headers, bodies and even request methods. The Request Transformer plugin offers several ways to transform parts of the request. While we’ve covered many of them, combining multiple transformations can yield powerful results that uniquely shape a request to fit your API.&lt;/p&gt;

&lt;p&gt;We didn’t cover URI transformations, which allow you to transform the upstream request URI based on the incoming request. You can imagine taking a long REST-compliant URI, chock-full of a chain of resources, ids, sub-resources and more ids. Using URI capturing and template values, the Request Transformer plugin can capture all of that data in the URI and write it into the request body instead, sending the request upstream to a general all-purpose endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Ready for Advanced Request Transformations?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The Kong Konnect enterprise-level &lt;a href="https://docs.konghq.com/hub/kong-inc/request-transformer-advanced/"&gt;Request Transformer Advanced plugin&lt;/a&gt; provides even more targeted transformations with regular expression matching, variables and substitutions.&lt;/p&gt;

&lt;p&gt;All in all, the ability to transform your requests at the gateway level &lt;em&gt;before&lt;/em&gt; they hit your upstream service is critical. Whether it’s because of deprecated specs, documentation-reality misalignment or data privacy and security concerns, having the flexibility to shape incoming requests—and to do so quickly and simply—might be exactly what your DevOps team needs to save the day in a pinch.&lt;/p&gt;

&lt;p&gt;Once you’ve successfully set up API gateway request transformation policies, you may find these other tutorials helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/jwt-kong-gateway"&gt;How to Use the Kong Gateway JWT Plugin for Service Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/kong-gateway-oauth2/"&gt;4 Steps to Authorizing Services With the Kong Gateway OAuth2 Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/getting-started-kuma-service-mesh/"&gt;Getting Started With Kuma Service Mesh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/kong-konnect"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Lrm3tu22--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/themes/konghq/assets/img/cta-shortcode/konnect.jpg" alt="Get Started with Kong Konnect - The only full stack connectivity platform for cloud native architectures"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://konghq.com/blog/api-gateway-request-transformation/"&gt;8 Common API Gateway Request Transformation Policies&lt;/a&gt; appeared first on &lt;a href="https://konghq.com"&gt;KongHQ&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>apigateway</category>
      <category>konggateway</category>
      <category>kongplugins</category>
    </item>
    <item>
      <title>20 Lines of JavaScript to Create a Kong Gateway Plugin</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Wed, 26 May 2021 13:00:48 +0000</pubDate>
      <link>https://dev.to/kong/building-a-kong-gateway-plugin-with-just-20-lines-of-javascript-449f</link>
      <guid>https://dev.to/kong/building-a-kong-gateway-plugin-with-just-20-lines-of-javascript-449f</guid>
      <description>&lt;p&gt;We recently sat down to discuss the language for the next &lt;a href="https://konghq.com/kong/"&gt;Kong Gateway&lt;/a&gt; Plugin Development Kit (PDK). Given the number of JavaScript developers in the world and the variety of libraries and debugging tools available, there was only one logical choice. I’m excited to share that with the &lt;a href="https://konghq.com/blog/kong-gateway-oss-2-4-released/"&gt;Kong Gateway (OSS) 2.4 release&lt;/a&gt;, that functionality is now available to you all!&lt;/p&gt;

&lt;p&gt;To show the power of the new &lt;a href="https://github.com/Kong/kong-js-pdk"&gt;JavaScript PDK&lt;/a&gt;, we’re going to implement a plugin that adds &lt;a href="https://xclacksoverhead.org/home/about"&gt;X-Clacks-Overhead&lt;/a&gt;, a non-standardized HTTP header based on the work of &lt;a href="https://en.wikipedia.org/wiki/Terry_Pratchett"&gt;Terry Pratchett&lt;/a&gt; to all responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bootstrapping Your Development Environment
&lt;/h2&gt;

&lt;p&gt;The JavaScript plugin support in Kong Gateway works by running a Node.js server on the same machine as Kong Gateway and passing messages back and forth using &lt;code&gt;msgpack&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This means that we need a development environment that can run both the Kong Gateway and a Node.js process. You can configure this on your local machine, but to make things easier, I’ve put together a &lt;code&gt;docker&lt;/code&gt;-based environment for you to use.&lt;/p&gt;

&lt;p&gt;It might take a minute or two to download the images and build our Node.js environment. I recommend running it now in the background as you keep reading:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git clone https://github.com/Kong/docker-kong-js-pdk
$ cd kong-js-pdk-dev
$ docker-compose build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Your First Plugin
&lt;/h2&gt;

&lt;p&gt;The configuration provided in the environment we created reads all plugins from the &lt;code&gt;plugins&lt;/code&gt; directory. It’s currently empty as we have not created our first plugin yet.&lt;/p&gt;

&lt;p&gt;The JavaScript PDK uses the name of the JS file as the name of the plugin. Let’s go ahead and create a file called &lt;code&gt;clacks.js&lt;/code&gt; in the &lt;code&gt;plugins&lt;/code&gt; directory with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ClacksPlugin {
  async access(kong) {
    await kong.response.setHeader(`X-Clacks-Overhead`, "GNU Terry Pratchett");
  }
}

module.exports = {
  Plugin: ClacksPlugin,
  Version: "0.1.0"
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;kong&lt;/code&gt; object passed into the &lt;code&gt;access&lt;/code&gt; method is an instance of the JavaScript PDK provided by the plugin server. This means that we do not need to require &lt;code&gt;kong-pdk&lt;/code&gt; in our plugins, as it is automatically made available.&lt;/p&gt;

&lt;p&gt;There are five phases available for HTTP requests in the life-cycle of a Kong Gateway request:&lt;/p&gt;

&lt;p&gt;-&lt;code&gt;certificate&lt;/code&gt; – Executed once per request when the connection is SSL/TLS enabled&lt;br&gt;
-&lt;code&gt;rewrite&lt;/code&gt; – Performed before the API gateway does any routing&lt;br&gt;
-&lt;code&gt;access&lt;/code&gt; – All routing is done, and the plugin knows which service the request is bound to. This is the last phase before the API gateway makes a request to upstream&lt;br&gt;
-&lt;code&gt;response&lt;/code&gt; – Allows you to manipulate the response from the upstream. Implementing this phase has a performance penalty as it enables request buffering&lt;br&gt;
-&lt;code&gt;log&lt;/code&gt; – Executed after the request has been completed&lt;/p&gt;
&lt;h2&gt;
  
  
  Enable the Plugin
&lt;/h2&gt;

&lt;p&gt;The environment we’re running uses Kong’s &lt;a href="https://docs.konghq.com/gateway-oss/2.4.x/db-less-and-declarative-config/#what-is-declarative-configuration"&gt;declarative config&lt;/a&gt; capability. That means that we need to update the config file to enable our new plugin. Open up &lt;code&gt;config/kong.yml&lt;/code&gt;, and you should see a service defined that proxies to mockbin.org:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  - name: example-service
    url: https://mockbin.org
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As our file name was &lt;code&gt;clacks.js&lt;/code&gt;, our plugin will be called &lt;code&gt;clacks&lt;/code&gt;. Let’s enable the plugin in the definition now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  - name: example-service
    url: https://mockbin.org
    plugins:
      - name: clacks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kong Gateway only allows you to use plugins that are on an allowlist for security purposes, so we’ll also need to add &lt;code&gt;clacks&lt;/code&gt; to that list. Open up &lt;code&gt;docker-compose.yml&lt;/code&gt; and edit the value of &lt;code&gt;KONG_PLUGINS&lt;/code&gt; so that it looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KONG_PLUGINS: bundled,clacks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Making a Request
&lt;/h2&gt;

&lt;p&gt;At this point the &lt;a href="https://konghq.com/learning-center/api-gateway/"&gt;API gateway&lt;/a&gt; is ready to run our new plugin, so let’s go ahead and start it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;docker-compose.yml&lt;/code&gt; file forwards the API gateway port to our local machine. That means we can make requests to &lt;code&gt;localhost:8000&lt;/code&gt; to test our service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -I localhost:8000

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: keep-alive
X-Clacks-Overhead: GNU Terry Pratchett
...snip...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can see the &lt;code&gt;X-Clacks-Overhead&lt;/code&gt; header in the response, which means that our plugin works as intended!&lt;/p&gt;

&lt;h2&gt;
  
  
  Making It Configurable
&lt;/h2&gt;

&lt;p&gt;The custom JavaScript plugin we built today is a simple plugin that does one thing and does it well. I want to take a moment to show you how you can make that behavior customizable using plugin configuration too.&lt;/p&gt;

&lt;p&gt;There is an ongoing discussion based on &lt;a href="https://tools.ietf.org/html/rfc6648"&gt;RFC 6648&lt;/a&gt; about if custom headers need an &lt;code&gt;X-&lt;/code&gt; prefix. Let’s make our plugin configurable so that people can decide if they want to use the &lt;code&gt;X-&lt;/code&gt; prefix.&lt;/p&gt;

&lt;p&gt;Plugin configuration is controlled using the &lt;code&gt;Schema property&lt;/code&gt; in &lt;code&gt;module.exports&lt;/code&gt; at the end of &lt;code&gt;clacks.js&lt;/code&gt;. Let’s add an entry to define a &lt;code&gt;use_prefix&lt;/code&gt; option that’s a boolean with a default value of &lt;code&gt;true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = {
  Plugin: ClacksPlugin,
  Schema: [{ use_prefix: { type: "boolean", default: true } }],
  Version: "0.1.0"
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any configuration provided to the plugin is passed in using the &lt;code&gt;constructor&lt;/code&gt;. Let’s go ahead and capture that in &lt;code&gt;clacks.js&lt;/code&gt; so that we can use it in our &lt;code&gt;access&lt;/code&gt; method and update &lt;code&gt;access&lt;/code&gt; so that it only adds the &lt;code&gt;X-&lt;/code&gt; prefix if &lt;code&gt;use_prefix&lt;/code&gt; is true:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ClacksPlugin {
  constructor(config) {
    this.config = config;
  }

  async access(kong) {
    const prefix = this.config.use_prefix ? "X-" : "";
    await kong.response.setHeader(
    `${prefix}Clacks-Overhead`,
    "GNU Terry Pratchett"
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run our plugin now, it will behave the same way as it did with a hardcoded &lt;code&gt;X-&lt;/code&gt; prefix. Let’s update our API gateway config in &lt;code&gt;config/kong.yml&lt;/code&gt; to set &lt;code&gt;use_prefix&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;services:
  - name: example-service
    url: https://mockbin.org
    plugins:
      - name: clacks
        config:
          use_prefix: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we restart our API gateway by pressing &lt;code&gt;Ctrl+C&lt;/code&gt; then running &lt;code&gt;docker-compose up&lt;/code&gt; again, we should now be able to make a request to &lt;code&gt;localhost:8000&lt;/code&gt; and see &lt;code&gt;Clacks-Overhead&lt;/code&gt; header without the &lt;code&gt;X-&lt;/code&gt; prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -I localhost:8000

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Clacks-Overhead: GNU Terry Pratchett
...snip...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Just 20 lines of Javascript, and we have a working Kong Gateway plugin, complete with configuration options!&lt;/p&gt;

&lt;p&gt;What we’ve built together is a trivial plugin, but using the environment provided and what you’ve learned about Kong’s configuration, you can go ahead and build plugins to your heart’s content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you’re looking for more plugin examples, take a look at some demo plugins:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/Kong/docker-kong-js-pdk/blob/69babbea0a34b4d8284e3c6eb978d979ae55bd71/plugins/json-to-yaml.js"&gt;Convert GitHub API responses from JSON to YAML&lt;/a&gt; (&lt;a href="https://github.com/Kong/docker-kong-js-pdk/blob/with-sample-plugins/config/kong.yml#L5-L12"&gt;config&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/Kong/docker-kong-js-pdk/blob/with-sample-plugins/plugins/pokemon-evolution.js"&gt;Extract a Pokemon evolution chain from PokeAPI&lt;/a&gt; (&lt;a href="https://github.com/Kong/docker-kong-js-pdk/blob/with-sample-plugins/config/kong.yml#L13-L20"&gt;config&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If you have any questions, post them on&lt;/strong&gt; &lt;a href="https://discuss.konghq.com/"&gt;&lt;strong&gt;Kong Nation&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To stay in touch, join the &lt;a href="https://konghq.com/community/"&gt;Kong Community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Once you’ve successfully set up a custom Kong plugin with JavaScript, you may find these other tutorials helpful:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/jwt-kong-gateway"&gt;How to Use the Kong Gateway JWT Plugin for Service Authentication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/kong-gateway-oauth2/"&gt;4 Steps to Authorizing Services With the Kong Gateway OAuth2 Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/getting-started-kuma-service-mesh/"&gt;Getting Started With Kuma Service Mesh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://docs.konghq.com/hub/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OEBfUbR8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://konghq.com/wp-content/themes/konghq/assets/img/cta-shortcode/kong-hub.jpg" alt="Visit Kong Hub - Extend Kong with 100+ powerful plugins, modules, and integrations"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://konghq.com/blog/kong-gateway-plugin-javascript/"&gt;Building a Kong Gateway Plugin with JavaScript&lt;/a&gt; appeared first on &lt;a href="https://konghq.com"&gt;KongHQ&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>apigateway</category>
      <category>javascript</category>
      <category>plugins</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dump Context Action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 21 May 2021 18:01:06 +0000</pubDate>
      <link>https://dev.to/mheap/dump-context-action-4nk8</link>
      <guid>https://dev.to/mheap/dump-context-action-4nk8</guid>
      <description>&lt;p&gt;This week's Action Spotlight introduces a new kind of action. It's the first time we're featuring a &lt;a href="https://docs.github.com/en/actions/creating-actions/creating-a-composite-run-steps-action"&gt;composite action&lt;/a&gt;, which allows you to include multiple workflow steps as a single &lt;code&gt;uses&lt;/code&gt; statement.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact Sheet&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Author&lt;/td&gt;
&lt;td&gt;crazy-max&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contributors&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stars&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repo&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/crazy-max/ghaction-dump-context"&gt;https://github.com/crazy-max/ghaction-dump-context&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketplace&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/marketplace/actions/dump-context"&gt;https://github.com/marketplace/actions/dump-context&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;This action outputs the following items from the runner environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;runner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;github&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;job&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;strategy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;matrix&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows you to see things like &lt;code&gt;github.event&lt;/code&gt; in the output to debug the data available to your actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;This is a composite action, which means that the whole thing is in a &lt;a href="https://github.com/crazy-max/ghaction-dump-context/blob/master/action.yml"&gt;single &lt;code&gt;action.yml&lt;/code&gt; file&lt;/a&gt;. It contains the fields that we'd expect to see, such as &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;author&lt;/code&gt; and &lt;code&gt;branding&lt;/code&gt;. However, it also has a value that we've not seen before; &lt;code&gt;runs.using: composite&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This tells Actions that the action provides steps that should be run as though they were provided in the workflow itself.&lt;/p&gt;

&lt;p&gt;There are seven steps, each dumping a different aspect of the environment. Each step uses the &lt;code&gt;toJson&lt;/code&gt; function to convert the input to a string that can be rendered in the output and stores that in the environment. Storing it in the environment prevents quoting issues when echoing out the value later.&lt;/p&gt;

&lt;p&gt;The characters before each heading (e.g. &lt;code&gt;\033[31;1;4m&lt;/code&gt;) are ANSI escape codes, which tells the output to make that text red, bold and underlined.&lt;/p&gt;

&lt;p&gt;Finally, each step has the shell set to &lt;code&gt;bash&lt;/code&gt;. This makes the action usable on all operating systems. Without this, the action would default to Powershell on Windows and fail.&lt;/p&gt;

&lt;p&gt;That's all there is to it - seven steps, all following the same pattern to output useful data in the logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common use cases
&lt;/h2&gt;

&lt;p&gt;There's only one use case - dump the current context to for debugging purposes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dump Context&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="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;dump&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Dump context&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;crazy-max/ghaction-dump-context@v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Auto-approve Workflow Action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 14 May 2021 09:17:57 +0000</pubDate>
      <link>https://dev.to/mheap/auto-approve-workflow-action-3m98</link>
      <guid>https://dev.to/mheap/auto-approve-workflow-action-3m98</guid>
      <description>&lt;p&gt;When GitHub disabled automatic workflow runs to prevent crypto miners it made some OSS maintainers lives harder, so I wrote a GitHub Action that automatically approves all pending workflow runs (so long as they don't edit &lt;code&gt;.github/workflows&lt;/code&gt;)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact Sheet&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Author&lt;/td&gt;
&lt;td&gt;mheap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contributors&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stars&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repo&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/mheap/automatic-approve-action"&gt;https://github.com/mheap/automatic-approve-action&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketplace&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/marketplace/actions/automatic-approve"&gt;https://github.com/marketplace/actions/automatic-approve&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;When a pull request is raised by a first time contributor to a repo, the any workflows that would usually be triggered are set to &lt;code&gt;pending&lt;/code&gt;. This is to prevent bad actors abusing actions for things like crypto mining.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LI2nNytp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/auto-approve-workflow-action/pr-awaiting-approval.png/4e293b09-640.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LI2nNytp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/auto-approve-workflow-action/pr-awaiting-approval.png/4e293b09-640.jpeg" alt="Pull request workflow awaiting approval"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a good thing in general, but it affects &lt;em&gt;all&lt;/em&gt; pull requests, not just those that change executables. This was the fastest way to fix the issue as any code that executes could be used to start a mining process.&lt;/p&gt;

&lt;p&gt;However, it also impacted projects such as the &lt;a href="https://github.com/OctoPrint/plugins.octoprint.org"&gt;OctoPrint plugin repository&lt;/a&gt; which are primarily non-executable metadata.&lt;/p&gt;

&lt;p&gt;This action is intended to run every 5 minutes and approve any pending workflow runs, allowing the maintainer to see all the information they need to review a PR such as linting and tests without having to wait for a build to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;This action runs on a schedule, fetching any pending workflow runs and automatically approving them if some constraints are met. It requires a personal access token as shown &lt;a href="https://docs.github.com/en/rest/reference/actions#approve-a-workflow-run-for-a-fork-pull-request"&gt;in the GitHub API docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Working through the action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L8"&gt;Read a Personal Access Token&lt;/a&gt; from the &lt;code&gt;token&lt;/code&gt; input&lt;/li&gt;
&lt;li&gt;Create a list of &lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L9-L14"&gt;allowed workflows&lt;/a&gt; to be automatically approved&lt;/li&gt;
&lt;li&gt;Create a list of &lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L16-L19"&gt;dangerous files&lt;/a&gt; and &lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L20"&gt;automatically add&lt;/a&gt; &lt;code&gt;.github/workflows&lt;/code&gt; to it&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L22-L27"&gt;Fetch the list of workflow runs&lt;/a&gt; with a status of &lt;code&gt;action_required&lt;/code&gt; in the current repository&lt;/li&gt;
&lt;li&gt;Filter down the list of pending workflows to &lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L35-L38"&gt;those which exist in the list of allowed workflows&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove any runs that &lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L35-L38"&gt;edit a file&lt;/a&gt; that is in the &lt;code&gt;dangerous_files&lt;/code&gt; list&lt;/li&gt;
&lt;li&gt;Finally, &lt;a href="https://github.com/mheap/automatic-approve-action/blob/e6d135ed725392625310e6882ae1e6be028e485d/index.js#L82-L95"&gt;approve any runs&lt;/a&gt; that are left in the list. These are runs that have an action of &lt;code&gt;status_required&lt;/code&gt;, are listed in the &lt;code&gt;workflows&lt;/code&gt; input and do not change any files in &lt;code&gt;dangerous_files&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common use cases
&lt;/h2&gt;

&lt;p&gt;There's only one use case for this action, which is to approve safe workflows to run automatically. This typically involves running processes such as linting or static site generation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Automatic Approve&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*/5&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;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&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;automatic-approve&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;Automatic Approve&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Automatic Approve&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;mheap/automatic-approve-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PAT }}&lt;/span&gt;
          &lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pr.yml,lint.yml"&lt;/span&gt;
          &lt;span class="na"&gt;dangerous_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;build.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Markdown Meta Action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 07 May 2021 18:01:58 +0000</pubDate>
      <link>https://dev.to/mheap/markdown-meta-action-4hok</link>
      <guid>https://dev.to/mheap/markdown-meta-action-4hok</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact Sheet&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Author&lt;/td&gt;
&lt;td&gt;mheap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contributors&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stars&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repo&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/mheap/markdown-meta-action"&gt;https://github.com/mheap/markdown-meta-action&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketplace&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/marketplace/actions/markdown-meta"&gt;https://github.com/marketplace/actions/markdown-meta&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;Content containing some &lt;code&gt;YAML&lt;/code&gt; frontmatter is very common nowadays (in fact, you're reading a post that uses it right now).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;markdown-meta&lt;/code&gt; allows you to read that metadata and makes it available as an output in your GitHub Actions workflow.&lt;/p&gt;

&lt;p&gt;The frontmatter for this file is 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="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Markdown Meta Action&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Read the frontmatter from your markdown files and use the values in your workflows&lt;/span&gt;
&lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;markdown-meta-action&lt;/span&gt;
&lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2021-05-07T19:01:58+01:00"&lt;/span&gt;
&lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Spotlight"&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;github-actions&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given that file we can run the following workflow that outputs the post title:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get Post Meta&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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;post-meta&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post Meta&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Markdown Meta&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;mheap/markdown-meta-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./_posts/markdown-meta-action.md&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;Output the post title&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;echo "${{ steps.meta.outputs.title }}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We use the &lt;code&gt;id&lt;/code&gt; parameter to name the &lt;code&gt;markdown-meta&lt;/code&gt; step &lt;code&gt;meta&lt;/code&gt; and then access the data returned by it using &lt;code&gt;steps.meta.outputs&lt;/code&gt;. Each key in the frontmatter is available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;steps.meta.outputs.title&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steps.meta.outputs.description&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steps.meta.outputs.slug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steps.meta.outputs.date&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steps.meta.outputs.category&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steps.meta.outputs.tags&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;This is a really tiny action, just 7 lines long:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It fetches the filename to read from the &lt;code&gt;file&lt;/code&gt; input and &lt;a href="https://github.com/mheap/markdown-meta-action/blob/3484e15d3b1ee6d1baec025e7a3167e728aea7b6/index.js#L7-L8"&gt;reads the content into a string&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next, it uses the &lt;code&gt;gray-matter&lt;/code&gt; library to &lt;a href="https://github.com/mheap/markdown-meta-action/blob/3484e15d3b1ee6d1baec025e7a3167e728aea7b6/index.js#L9"&gt;extract the frontmatter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mheap/markdown-meta-action/blob/3484e15d3b1ee6d1baec025e7a3167e728aea7b6/index.js#L10-L12"&gt;Then for each value in the frontmatter, it sets an output&lt;/a&gt;. The key name is run through &lt;code&gt;slugify&lt;/code&gt; to remove special characters and whitespace

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;a key with spaces&lt;/code&gt; becomes &lt;code&gt;a-key-with-spaces&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;t!tle&lt;/code&gt; becomes &lt;code&gt;ttle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;🚀 lift-off&lt;/code&gt; becomes &lt;code&gt;lift-off&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As it uses &lt;code&gt;core.setOutput()&lt;/code&gt; any complex data types such as objects or arrays are JSON encoded before being output. This means that in the above example, &lt;code&gt;tags&lt;/code&gt; will be &lt;code&gt;["github-actions"]&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common use cases
&lt;/h2&gt;

&lt;p&gt;The best use case that I can think of for this action is automated blog post updates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get Post Meta&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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;post-meta&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post Meta&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get latest post&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;cd _posts&lt;/span&gt;
          &lt;span class="s"&gt;echo "::set-output name=post_name::$(ls -rt | tail -n 1)"&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest_post&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;Markdown Meta&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;mheap/markdown-meta-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./_posts/${{ steps.latest_post.outputs.post_name }}&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;ethomson/send-tweet-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;just&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;published!&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;steps.meta.outputs.title&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;https://michaelheap.com/${{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;steps.meta.outputs.slug&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
          &lt;span class="na"&gt;consumer-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TWITTER_CONSUMER_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;consumer-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TWITTER_CONSUMER_API_SECRET }}&lt;/span&gt;
          &lt;span class="na"&gt;access-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TWITTER_ACCESS_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;access-token-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Generating OpenGraph images with Netlify On-demand builders</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Sat, 01 May 2021 16:08:35 +0000</pubDate>
      <link>https://dev.to/mheap/generating-opengraph-images-with-netlify-on-demand-builders-5266</link>
      <guid>https://dev.to/mheap/generating-opengraph-images-with-netlify-on-demand-builders-5266</guid>
      <description>&lt;p&gt;A little while back I built an OpenGraph image generator on AWS Lambda for my &lt;a href="https://michaelheap.com/category/action-spotlight"&gt;Action Spotlight&lt;/a&gt; and &lt;a href="https://michaelheap.com/til"&gt;TIL&lt;/a&gt; categories. I toyed with the idea of making them generate the images on demand, but decided that I didn’t want the potential traffic to a Lamba function and opted to pre-generate and store in S3 instead.&lt;/p&gt;

&lt;p&gt;Then this week, I saw that &lt;a href="https://docs.netlify.com/configure-builds/on-demand-builders/"&gt;Netlify On-demand builders&lt;/a&gt; are available in beta which solve all my problems. I don’t need to pre-generate any images, but they’re also only generated once per deploy and distributed via the Netlify CDN.&lt;/p&gt;

&lt;p&gt;My requirements for the project were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each image should only be generated once (once per deploy is fine)&lt;/li&gt;
&lt;li&gt;The function should support multiple templates for my OpenGraph image&lt;/li&gt;
&lt;li&gt;It should be possible to pass in any data required&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, it was a success 🎉&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you’re looking for the code without an explanation, it’s available &lt;a href="https://github.com/mheap/netlify-opengraph-on-demand-builders"&gt;on GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Structuring your function
&lt;/h2&gt;

&lt;p&gt;Netlify functions live in the &lt;code&gt;netlify/functions&lt;/code&gt; directory in your project. You can create files directly in there, or create a folder named after your function. I chose to create a folder as I have some additional files to package up with my JS file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; netlify/functions/ogtouch netlify/functions/og/og.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used my &lt;a href="https://michaelheap.com/netlify-function-lambda-return-image/"&gt;image generation via Netlify&lt;/a&gt; script as a starting point as I knew it worked to generate an image.&lt;/p&gt;

&lt;p&gt;The first requirement is that each image should only be generated once. To enable this you use the &lt;code&gt;builder&lt;/code&gt; decorator from &lt;code&gt;@netlify/functions&lt;/code&gt;:&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;}&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;@netlify/functions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Code&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to install it into your project with &lt;code&gt;npm install --save-dev @netlify/functions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point the image will be generated on the first execution and then cached for any future requests.&lt;/p&gt;

&lt;p&gt;The image so far looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WNysz6Ip--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/og-image-netlify-on-demand-builders/basic-image.png/e191dc25-640.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WNysz6Ip--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/og-image-netlify-on-demand-builders/basic-image.png/e191dc25-640.jpeg" alt="Initial image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Template support
&lt;/h2&gt;

&lt;p&gt;Next, I wanted to add template support so that I didn’t have to hard-code the entire template in my function.&lt;/p&gt;

&lt;p&gt;To do this I created a folder named &lt;code&gt;templates&lt;/code&gt; and a template for my #til category:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; netlify/functions/og/templatestouch netlify/functions/og/templates/til.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To load this file I imported the &lt;code&gt;fs&lt;/code&gt; module and read the file contents into a variable, then passed that string to &lt;code&gt;page.setContent()&lt;/code&gt;:&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;fs&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;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...snip...&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;til&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;htmlPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./templates/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.html`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&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;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlPage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contents of &lt;code&gt;til.html&lt;/code&gt; can be anything you like. For testing purposes, I set it to the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;red&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;{title}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;{description}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The title and description placeholders don’t do anything yet, but we’ll use those later. For now, it’ll just render the page exactly as it is.&lt;/p&gt;

&lt;p&gt;At this point you can deploy the function and it will render the HTML template provided as-is&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Df0pQo6G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/og-image-netlify-on-demand-builders/using-template.png/40b2bcee-640.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Df0pQo6G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/og-image-netlify-on-demand-builders/using-template.png/40b2bcee-640.jpeg" alt="Image using a template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing in data
&lt;/h2&gt;

&lt;p&gt;Usually we’d use &lt;code&gt;GET&lt;/code&gt; parameters to pass in the title and description to use in the image, but on-demand builders _ don’t provide access to HTTP headers or query parameters from incoming requests_.&lt;/p&gt;

&lt;p&gt;This means that we have to make the parameters a part of the URI. I thought about the best way to achieve this, and decided on a URI structure that looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/.netlify/functions/og/template=til/title=Demo Title/description=This is an example description that is a little longer to see how it works out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything after &lt;code&gt;/og&lt;/code&gt; will be ignored, but it allows us to map over each segment in the URI and treat it as a key/value pair.&lt;/p&gt;

&lt;p&gt;To build this mapping, add the following code to your function:&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="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;=&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;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;decodeURIComponent&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;s&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;s&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// snip...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll notice that we also read &lt;code&gt;template&lt;/code&gt; from the path, so you can delete &lt;code&gt;const template = "til";&lt;/code&gt; from your function too.&lt;/p&gt;

&lt;p&gt;Finally, we need to swap out the placeholders in the template for the values in &lt;code&gt;params&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;htmlPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./templates/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.html`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;for&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;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;htmlPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;htmlPage&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="s2"&gt;`{&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;k&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="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&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;After committing and deploying that, I can visit &lt;code&gt;/.netlify/functions/og/template=til/title=Demo Title/description=This is an example description that is a little longer to see how it works out&lt;/code&gt; and see that it renders the image as expected&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iCbKH7Y8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/og-image-netlify-on-demand-builders/final-image.png/c7ffd533-640.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iCbKH7Y8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://michaelheap.com/images/og-image-netlify-on-demand-builders/final-image.png/c7ffd533-640.jpeg" alt="Rendered image using passed in data"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Under 45 lines of code and we have a Netlify function that generates OpenGraph images on the fly and caches them on a CDN for future requests. On-demand builders seem pretty great, and I’m looking forward to working with them more in the future.&lt;/p&gt;

&lt;p&gt;If you're looking for the full code, it's available below or &lt;a href="https://github.com/mheap/netlify-opengraph-on-demand-builders"&gt;on GitHub&lt;/a&gt;&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;chromium&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;chrome-aws-lambda&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;puppeteer&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;puppeteer-core&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;}&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;@netlify/functions&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;fs&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;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&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;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;=&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;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;decodeURIComponent&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;s&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;s&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&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;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;defaultViewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;630&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;htmlPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`./templates/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.html`&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;for&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;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;htmlPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;htmlPage&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="s2"&gt;`{&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;k&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="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlPage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;waitForTimeout&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&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;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;isBase64Encoded&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="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>netlify</category>
      <category>serverless</category>
    </item>
    <item>
      <title>YAMLer Action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 30 Apr 2021 09:40:13 +0000</pubDate>
      <link>https://dev.to/mheap/yamler-action-1256</link>
      <guid>https://dev.to/mheap/yamler-action-1256</guid>
      <description>&lt;p&gt;When running your workflows, sometimes you need to read information from a file in your repository. &lt;code&gt;YAMLer&lt;/code&gt; parses the provided YAML file and makes the values in the file available as output variables.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact Sheet&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Author&lt;/td&gt;
&lt;td&gt;juliojimenez&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contributors&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stars&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repo&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/juliojimenez/yamler"&gt;https://github.com/juliojimenez/yamler&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketplace&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/marketplace/actions/yamler"&gt;https://github.com/marketplace/actions/yamler&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;YAMLer&lt;/code&gt; takes each field in the provided YAML document and makes it available as an output variable. Nested resources are nested using a double underscore (&lt;code&gt;__&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Take the following YAML file as an input:&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;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;This is a description&lt;/span&gt;
&lt;span class="na"&gt;contributor&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;Michael&lt;/span&gt;
  &lt;span class="na"&gt;handle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mheap&lt;/span&gt;
&lt;span class="na"&gt;projects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;actions-book&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;github-default-branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These variables would be available 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="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;This is a description&lt;/span&gt;
&lt;span class="na"&gt;contributor__name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Michael&lt;/span&gt;
&lt;span class="na"&gt;contributor__handle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mheap&lt;/span&gt;
&lt;span class="na"&gt;contributor__projects__0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions-book&lt;/span&gt;
&lt;span class="na"&gt;contributor__projects__1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-default-branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action works for deeply nested values and has been tested with scalars (strings, numbers, booleans), objects and arrays.&lt;/p&gt;

&lt;p&gt;In addition to making nested properties available, the action also handles special characters.&lt;/p&gt;

&lt;p&gt;Using the sample from the &lt;a href="http://yaml.org/"&gt;YAML.org&lt;/a&gt; test file, we can see that periods and parenthesis are replaced:&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;YAML Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;YAML 1.2 (3rd Edition)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://yaml.org/spec/1.2/spec.html&lt;/span&gt;
  &lt;span class="s"&gt;YAML 1.1 (2nd Edition)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://yaml.org/spec/1.1/&lt;/span&gt;
  &lt;span class="s"&gt;YAML 1.0 (1st Edition)&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://yaml.org/spec/1.0/&lt;/span&gt;
  &lt;span class="na"&gt;YAML Issues Page&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/yaml/yaml/issues&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is made available as:&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;yaml_resources__yaml_1_2_3rd_edition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://yaml.org/spec/1.2/spec.html&lt;/span&gt;
&lt;span class="na"&gt;yaml_resources__yaml_1_1_2nd_edition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://yaml.org/spec/1.1/&lt;/span&gt;
&lt;span class="na"&gt;yaml_resources__yaml_1_0_1st_edition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://yaml.org/spec/1.0/&lt;/span&gt;
&lt;span class="na"&gt;yaml_resources__yaml_issues_page&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/yaml/yaml/issues&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the periods are replaced with underscores, and the parenthesis are removed? Let’s take a look at the rules for replacement in the source!&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;The entire action is available &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts"&gt;in a single file&lt;/a&gt; which makes it easy to understand what’s going on.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L90-L100"&gt;entry point&lt;/a&gt; reads the file provided in the &lt;code&gt;yaml-file&lt;/code&gt; input and passes it to &lt;code&gt;traverseObject&lt;/code&gt; to start building the output. This method &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L30"&gt;iterates over the keys&lt;/a&gt; in the provided YAML file and decides how to process each entry.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the value &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L32-L37"&gt;is a scalar&lt;/a&gt; (string, number or boolean) we’re at the end of a nested value (the leaf) and it’s time to &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L38-L42"&gt;add an output&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If the value &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L43"&gt;is an object or an array&lt;/a&gt;, we &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L44"&gt;keep track of all the keys we saw&lt;/a&gt; whilst traversing to this depth and then call either &lt;code&gt;traverseArray&lt;/code&gt; or &lt;code&gt;traverseObject&lt;/code&gt; as required.

&lt;ul&gt;
&lt;li&gt;Calling &lt;code&gt;traverseArray&lt;/code&gt; &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L56-L83"&gt;loops over all values in the array&lt;/a&gt; and emits a new value if we detect a scalar&lt;/li&gt;
&lt;li&gt;Calling &lt;code&gt;traverseObject&lt;/code&gt; is &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L48"&gt;a recursive call&lt;/a&gt; which starts this set of bullet points again with the child value passed in as the argument&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whilst processing keys, the action applies the following rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L13"&gt;Replace whitespace, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;.&lt;/code&gt; or &lt;code&gt;:&lt;/code&gt; with an underscore&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L17"&gt;Remove parenthesis &lt;code&gt;()&lt;/code&gt;, square brackets &lt;code&gt;[]&lt;/code&gt;, single quotes and commas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L21"&gt;Replace &lt;code&gt;+&lt;/code&gt; with &lt;code&gt;p&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/src/index.ts#L25"&gt;Replace &lt;code&gt;#&lt;/code&gt; with &lt;code&gt;s&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last two substitutions seem strange to me, but I guess the author had a use case where it made sense.&lt;/p&gt;

&lt;p&gt;As mentioned earlier, the action has a &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/action.yml#L4-L6"&gt;single required input&lt;/a&gt; which is the path to the YAML file to read. &lt;a href="https://github.com/juliojimenez/yamler/blob/815b577e88e9ddf583be1d80d98443571b303677/action.yml#L7-L9"&gt;It runs using the &lt;code&gt;node12&lt;/code&gt; runtime&lt;/a&gt; thanks to a compiled version of the action being available in the &lt;code&gt;build&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Finally, the action uses semantic versioning. It’s currently at &lt;code&gt;v0&lt;/code&gt; whilst it’s in beta, and uses the &lt;a href="https://github.com/technote-space/release-github-actions"&gt;release-github-actions&lt;/a&gt; action to automatically build and commit an updated &lt;code&gt;build/index.js&lt;/code&gt; file when a release is created. Then the major and minor tags are updated to point at the new release commit.&lt;/p&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>GitHub Slug Action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 23 Apr 2021 18:41:27 +0000</pubDate>
      <link>https://dev.to/mheap/github-slug-action-5dab</link>
      <guid>https://dev.to/mheap/github-slug-action-5dab</guid>
      <description>&lt;p&gt;We're looking at a different kind of action today! This action doesn't actually &lt;em&gt;do&lt;/em&gt; anything. Instead, it makes more information available in your workflow and the environment for other actions to use.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact Sheet&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Author&lt;/td&gt;
&lt;td&gt;rlespinasse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contributors&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stars&lt;/td&gt;
&lt;td&gt;106&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repo&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/rlespinasse/github-slug-action"&gt;https://github.com/rlespinasse/github-slug-action&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marketplace&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/marketplace/actions/github-slug-action"&gt;https://github.com/marketplace/actions/github-slug-action&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;github-slug-ref&lt;/code&gt; action provides information that’s commonly needed in workflows in a useful format. By default, variables such as &lt;code&gt;GITHUB_REF&lt;/code&gt; are in the format &lt;code&gt;refs/heads/main&lt;/code&gt; or &lt;code&gt;refs/tags/v1.0.0&lt;/code&gt;. This catches people out as they’re not used to thinking about if what they’re referencing is a branch or a tag, and they’re definitely not used to the &lt;code&gt;refs&lt;/code&gt; prefix.&lt;/p&gt;

&lt;p&gt;Once the &lt;code&gt;rlespinasse/github-slug-action&lt;/code&gt; action has been included in your workflow, some new environment variables will be available. To include it, add a &lt;code&gt;uses&lt;/code&gt; entry within your &lt;code&gt;steps&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Slug Demo&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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;demo&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;rlespinasse/github-slug-action@v3.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we think about &lt;code&gt;GITHUB_REF&lt;/code&gt; from above, &lt;code&gt;GITHUB_REF_SLUG&lt;/code&gt; is available after including this action. If &lt;code&gt;GITHUB_REF&lt;/code&gt; was &lt;code&gt;refs/heads/main&lt;/code&gt;, &lt;code&gt;GITHUB_REF_SLUG&lt;/code&gt; will be &lt;code&gt;main&lt;/code&gt;. If the input was &lt;code&gt;refs/tags/v1.0.0&lt;/code&gt;, &lt;code&gt;GITHUB_REF_SLUG&lt;/code&gt; will be &lt;code&gt;v1.0.0&lt;/code&gt;. If you’d like to see a full list of transformations, see the &lt;a href="https://github.com/rlespinasse/github-slug-action/blob/v3.x/docs/slug-variables.md"&gt;slug variables&lt;/a&gt; docs. If you’re looking for slugs that are safe to use in a URL, you might be interested in the &lt;a href="https://github.com/rlespinasse/github-slug-action/blob/v3.x/docs/slug-url-variables.md"&gt;URL slug variables&lt;/a&gt; too.&lt;/p&gt;

&lt;p&gt;Another feature of the action is &lt;em&gt;short variables&lt;/em&gt;. In addition to the full &lt;code&gt;sha&lt;/code&gt; (available in &lt;code&gt;GITHUB_SHA&lt;/code&gt;), this action will make an 8 character prefix available in &lt;code&gt;GITHUB_SHA_SHORT&lt;/code&gt;. This can be useful is you need a semi-unique identifier and want a shorter key than the full &lt;code&gt;sha&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In addition to manipulating the existing environment variables, the action also makes some of the information from &lt;code&gt;github.event&lt;/code&gt; available as an environment variable. For example, &lt;code&gt;github.event.pull_request.head.sha&lt;/code&gt; is available as &lt;code&gt;GITHUB_EVENT_PULL_REQUEST_HEAD_SHA_SHORT&lt;/code&gt;. This is useful when you’re writing bash scripts and can’t parse &lt;code&gt;event.json&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;Looking at the source code, this action is a large collection of regular expressions. It handles cases such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rlespinasse/github-slug-action/blob/v3.x/src/slug.ts#L106-L108"&gt;Replacing any non-alphanumeric character&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rlespinasse/github-slug-action/blob/v3.x/src/slug.ts#L114-L116"&gt;Removing refs/* from GITHUB_REF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rlespinasse/github-slug-action/blob/v3.x/src/slug.ts#L1"&gt;Ensure that all slugs are less than 63 characters long&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to see all of the variables it’s creating in the source code, they’re all &lt;a href="https://github.com/rlespinasse/github-slug-action/blob/v3.x/src/main.ts#L107-L170"&gt;available in &lt;code&gt;main.ts&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The action itself is compiled and &lt;a href="https://github.com/rlespinasse/github-slug-action/tree/v3.x/dist"&gt;pushed to the repo&lt;/a&gt; so that people can use it directly. This isn't a pattern I like personally (I prefer to use the &lt;a href="http://localhost:8080/build-and-tag-action/"&gt;build and tag action&lt;/a&gt;) but it means that consumers can use the javascript runtime so it's a reasonable trade off.&lt;/p&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Create or Update PR Action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 16 Apr 2021 16:45:23 +0000</pubDate>
      <link>https://dev.to/mheap/create-or-update-pr-action-2bd7</link>
      <guid>https://dev.to/mheap/create-or-update-pr-action-2bd7</guid>
      <description>&lt;p&gt;Following on from our theme of actions to commit and push changes, today’s action can commit and push any local edits &lt;em&gt;and&lt;/em&gt; raise a pull request automatically.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Fact Sheet&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Author&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/gr2m"&gt;gr2m&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Contributors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stars&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/gr2m/create-or-update-pull-request-action"&gt;https://github.com/gr2m/create-or-update-pull-request-action&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Marketplace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/marketplace/actions/create-or-update-pull-request"&gt;https://github.com/marketplace/actions/create-or-update-pull-request&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;A GitHub Action to create or update a pull request based on local changes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can use the action to add, commit and push changes to a remote branch and create a pull request for those changes automatically. It can add all your files in a single commit, add multiple commits to a single PR and automatically add labels and assignees to any pull requests that it raises.&lt;/p&gt;

&lt;p&gt;Oh, and it’s built by &lt;a href="https://github.com/gr2m"&gt;Gregor&lt;/a&gt; which means I instantly trust it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;If there any uncommitted changes, files matching the pattern provided in the &lt;code&gt;path&lt;/code&gt; input supplied are added and committed. You can customise the commit with the &lt;code&gt;commit-message&lt;/code&gt; and &lt;code&gt;author&lt;/code&gt; inputs&lt;/li&gt;
&lt;li&gt;If there are any local commits (created by step 1, or by any other means) the action pushes changes to a remote branch with the name specified in the &lt;code&gt;branch&lt;/code&gt; input&lt;/li&gt;
&lt;li&gt;Search for any open pull requests with &lt;code&gt;branch&lt;/code&gt; as the HEAD commit using the search query &lt;code&gt;head:${inputs.branch} type:pr is:open repo:${process.env.GITHUB_REPOSITORY}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If there is an existing pull request, the action stops execution. Otherwise, a new pull request is created with the &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; inputs provided. Any &lt;code&gt;labels&lt;/code&gt; and &lt;code&gt;assignees&lt;/code&gt; provided as inputs are automatically added&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using the search API to check if there is already an open pull request for a branch is a fantastic solution. When I needed to do the same, I uses the &lt;code&gt;pulls.list&lt;/code&gt; endpoint and passed in the &lt;code&gt;head&lt;/code&gt; parameter like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pulls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;head&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="nx"&gt;owner&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="nx"&gt;automationBranchName&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="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// Create a PR}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I find interesting about this action is that it only uses the GitHub API for actions that it cannot perform using the &lt;code&gt;git&lt;/code&gt; binary. There is a &lt;code&gt;runShellCommand&lt;/code&gt; function that is used to run &lt;code&gt;git fetch&lt;/code&gt;, &lt;code&gt;git commit&lt;/code&gt; etc on the runner directly. When automating updates I’ve used the GitHub API to get and modify file contents to remove the requirement to use &lt;code&gt;actions/checkout&lt;/code&gt; as part of your workflow, but in this context you’re likely to have a workspace so it makes sense to interact directly with the files.&lt;/p&gt;

&lt;p&gt;Another interesting choice is that the action does not configure a remote or add authentication details, instead opting to specify both values directly in the &lt;code&gt;git&lt;/code&gt; commit like so:&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;await&lt;/span&gt; &lt;span class="nx"&gt;runShellCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s2"&gt;`git push -f https://x-access-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;@github.com/&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_REPOSITORY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.git HEAD:refs/heads/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;branch&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s nothing in the commit history to explain why, but if I had to hazard a guess it’s due to the fact that other processes can interact with &lt;code&gt;git&lt;/code&gt; config files, so by passing the details directly to the command it reduces the chances of anything interfering.&lt;/p&gt;

&lt;p&gt;Finally, this action provides an example of how to provide lists of values in &lt;code&gt;inputs&lt;/code&gt;. The action uses a comma-separated list of values for &lt;code&gt;labels&lt;/code&gt; and &lt;code&gt;assignees&lt;/code&gt; to provide multiple values. There still isn’t consensus how to provide multiple values (some actions provide a JSON list and deserialise), but this is another vote for the “comma-separated” camp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common use cases
&lt;/h2&gt;

&lt;p&gt;This action is most useful when you’ve got a task running on a schedule that fetches and commits updated data to the repo. If there are no changes, no commit or pull request will be created.&lt;/p&gt;

&lt;p&gt;The README itself shows that it’s being used to keep track of &lt;a href="https://github.com/sinchang/cn-starbucks-stores-data/blob/master/.github/workflows/update.yml"&gt;Chinese Starbucks stores&lt;/a&gt;, &lt;a href="https://github.com/ergebnis/composer-normalize/blob/69ec6fd9a87cbb16badf2a988f4372221592b05e/.github/workflows/schema.yml#L25-L38"&gt;download JSON schema updates periodically&lt;/a&gt; and even &lt;a href="https://github.com/nodejs/node/blob/master/.github/workflows/license-builder.yml"&gt;by the Node project itself&lt;/a&gt; to keep the license file up to date.&lt;/p&gt;

&lt;p&gt;If you’re interested in learning more about this pattern, there’s a great post by &lt;a href="https://simonwillison.net/2020/Oct/9/git-scraping/"&gt;Simon Willison&lt;/a&gt; on &lt;code&gt;git scraping&lt;/code&gt; as a concept.&lt;/p&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>GitHub Push Action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 26 Mar 2021 19:59:46 +0000</pubDate>
      <link>https://dev.to/mheap/github-push-action-1ad2</link>
      <guid>https://dev.to/mheap/github-push-action-1ad2</guid>
      <description>&lt;p&gt;Today we’re looking at &lt;a href="https://github.com/ad-m/github-push-action"&gt;ad-m/github-push-action&lt;/a&gt;, the third and final action related to pushing changes made during a GitHub Actions workflow run back to a repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;Unlike &lt;code&gt;git-auto-commit-action&lt;/code&gt; and &lt;code&gt;add-and-push&lt;/code&gt;, &lt;code&gt;github-push-action&lt;/code&gt; doesn’t handle committing files for you. It expects your workflow to add and commit any files you need and handles the act of pushing changes to GitHub.&lt;/p&gt;

&lt;p&gt;You might be thinking that this seems like an odd action to build; surely it’s as simple as running &lt;code&gt;git push&lt;/code&gt; and it works? Unfortunately not! The repository provided in your workspace doesn’t have permission to push changes back so you need to configure your repo to use the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; provided in order to have write permission.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;This action is an interesting one as it uses both &lt;code&gt;javascript&lt;/code&gt; and &lt;code&gt;bash&lt;/code&gt; components to get the job done. &lt;code&gt;start.js&lt;/code&gt; is the main entry point, which allows the action to run &lt;code&gt;start.sh&lt;/code&gt; on Linux, MacOS and Windows runners.&lt;/p&gt;

&lt;p&gt;Let’s take a look at &lt;code&gt;start.js&lt;/code&gt; first as it’s the main entry point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.js#L43-L44"&gt;The action accepts&lt;/a&gt; both &lt;code&gt;branch&lt;/code&gt; and &lt;code&gt;repository&lt;/code&gt; as inputs.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;repository&lt;/code&gt; isn’t provided &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.js#L44"&gt;it defaults&lt;/a&gt; to &lt;code&gt;GITHUB_REPOSITORY&lt;/code&gt; in the environment. If it is provided, that will be used as the destination repository, allowing your workflow to push to other repositories.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;branch&lt;/code&gt; is provided it is used as-is. If it is not provided the script uses the GitHub API to &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.js#L46-L51"&gt;fetch the default branch&lt;/a&gt; for the repo and uses that as the input value&lt;/li&gt;
&lt;li&gt;Finally, it &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.js#L53-L59"&gt;executes&lt;/a&gt; &lt;code&gt;start.sh&lt;/code&gt; ensuring that both &lt;code&gt;INPUT_BRANCH&lt;/code&gt; and &lt;code&gt;INPUT_REPOSITORY&lt;/code&gt; have values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What’s interesting to note here is that &lt;code&gt;start.js&lt;/code&gt; does not have any dependencies. It uses the &lt;code&gt;http&lt;/code&gt; module for &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.js#L6-L22"&gt;making HTTP requests&lt;/a&gt; and the &lt;code&gt;child_process&lt;/code&gt; module to &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.js#L24-L36"&gt;execute&lt;/a&gt;&lt;code&gt;start.sh&lt;/code&gt;. &lt;code&gt;actions/exec&lt;/code&gt; is typically used for this use case, but &lt;code&gt;github-push-action&lt;/code&gt; does not use any dependencies so that there is no build step required for the JS action.&lt;/p&gt;

&lt;p&gt;Now, on to &lt;code&gt;start.sh&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We see &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.sh#L4-L8"&gt;some new inputs&lt;/a&gt; that have defaults set in the script&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INPUT_FORCE&lt;/code&gt; (default &lt;code&gt;false&lt;/code&gt;) which will run &lt;code&gt;git push --force&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INPUT_TAGS&lt;/code&gt; (default &lt;code&gt;false&lt;/code&gt;) which will run &lt;code&gt;git push --tags&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INPUT_DIRECTORY&lt;/code&gt; (default &lt;code&gt;.&lt;/code&gt;, the current directory) which controls which directory the push is run from&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;REPOSITORY&lt;/code&gt;, which is set to &lt;code&gt;INPUT_REPOSITORY&lt;/code&gt; or &lt;code&gt;GITHUB_REPOSITORY&lt;/code&gt;. This is a &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.js#L44"&gt;duplication of the logic&lt;/a&gt; in &lt;code&gt;start.js&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; secret is required to push to the repo, so we &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.sh#L11-L14"&gt;check that it’s been set&lt;/a&gt; as &lt;code&gt;INPUT_GITHUB_TOKEN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;INPUT_FORCE&lt;/code&gt; is true then add &lt;code&gt;--force&lt;/code&gt; &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.sh#L16-L18"&gt;to the push options&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;INPUT_TAGS&lt;/code&gt; is set then add &lt;code&gt;--tags&lt;/code&gt; &lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.sh#L20-L22"&gt;to the push options&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.sh#L26"&gt;Configure the remote URL&lt;/a&gt; using the &lt;code&gt;user:password@github.com&lt;/code&gt; format&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ad-m/github-push-action/blob/057a6ba835d986bfe495dd476a6c4db1d5f9503c/start.sh#L28"&gt;Push the current&lt;/a&gt; &lt;code&gt;HEAD&lt;/code&gt; commit to &lt;code&gt;INPUT_BRANCH&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common use cases
&lt;/h2&gt;

&lt;p&gt;As with the last post, this action is most useful for committing back changed files such as style fixes or build artefacts.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;eslint&lt;/code&gt; and commit back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint source code&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint with ESLint&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&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 install&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;Update source code&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;eslint "src/**" --fix&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;Commit files&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;git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.name "github-actions[bot]"&lt;/span&gt;
          &lt;span class="s"&gt;git add src&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "ESLint fixes"&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;Push changes&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;ad-m/github-push-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;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="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.ref }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or build your project and commit back the files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Automatic Compile&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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;run&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;Compile&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&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 install&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;Compile&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 build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Commit files&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;git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"&lt;/span&gt;
          &lt;span class="s"&gt;git config --local user.name "github-actions[bot]"&lt;/span&gt;
          &lt;span class="s"&gt;git add lib&lt;/span&gt;
          &lt;span class="s"&gt;git commit -m "Built files"&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;Push changes&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;ad-m/github-push-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;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="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.ref }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/github-push"&gt;GitHub Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ad-m/github-push-action"&gt;GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Add and Commit action</title>
      <dc:creator>Michael Heap</dc:creator>
      <pubDate>Fri, 19 Mar 2021 09:09:39 +0000</pubDate>
      <link>https://dev.to/mheap/add-and-commit-action-1nh6</link>
      <guid>https://dev.to/mheap/add-and-commit-action-1nh6</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/EndBug/add-and-commit"&gt;Add and Commit&lt;/a&gt; is the second (of three!) actions that can be used to push your changes to a repo during a GitHub Actions workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it do?
&lt;/h2&gt;

&lt;p&gt;Much like the &lt;a href="https://michaelheap.com/git-auto-commit"&gt;last spotlight post&lt;/a&gt;, today’s action is focused on adding and committing files to a repo. The README does a good job explaining how it’s different to &lt;code&gt;git-auto-commit-action&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is heavily inspired by git-auto-commit-action (by Stefan Zweifel): that action automatically detects changed files and commits them. While this is useful for most situations, this doesn’t commit untracked files and can sometimes commit unintended changes (such as package-lock.json or similar, that may have happened during previous steps).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s also evolved from &lt;code&gt;git-auto-commit&lt;/code&gt; and supports pulling changes if required before a push, removing files with &lt;code&gt;git rm&lt;/code&gt; and makes features such as &lt;code&gt;signoff&lt;/code&gt; an input rather than making you specify &lt;code&gt;--signoff&lt;/code&gt; as a &lt;code&gt;commit_option&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it work?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;add-and-commit&lt;/code&gt; is implemented in TypeScript rather than &lt;code&gt;bash&lt;/code&gt; and is a single 388 line file named &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts"&gt;main.ts&lt;/a&gt;. It uses &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/package.json#L36"&gt;simple-git&lt;/a&gt; to interact with the underlying &lt;code&gt;git&lt;/code&gt; repo and provides a &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/lib/index.js"&gt;compiled version of the action&lt;/a&gt; that includes all dependencies.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main.ts&lt;/code&gt; itself is reasonable easy to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L23-L26"&gt;Add any files&lt;/a&gt; that match the pattern provided in &lt;code&gt;add&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L28-L31"&gt;Remove any files&lt;/a&gt; if &lt;code&gt;remove&lt;/code&gt; was provided&lt;/li&gt;
&lt;li&gt;If there are no staged files, &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L159-L162"&gt;the action stops execution&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Otherwise &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L38-L44"&gt;it populates&lt;/a&gt; &lt;code&gt;.git/config&lt;/code&gt; using the &lt;code&gt;author_email&lt;/code&gt; and &lt;code&gt;author_username&lt;/code&gt; provided&lt;/li&gt;
&lt;li&gt;As GitHub Actions does a shallow clone, the action then runs &lt;code&gt;git fetch&lt;/code&gt; to &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L46"&gt;get all branches&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;This isn’t documented in the README, but the action then &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L49-L51"&gt;switches to a branch&lt;/a&gt; with the name specified in the &lt;code&gt;branch&lt;/code&gt; input. This defaults to the branch that triggered the workflow run&lt;/li&gt;
&lt;li&gt;Then it &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L53-L64"&gt;ensures that the branch is up to date&lt;/a&gt;. You can choose &lt;code&gt;NO-PULL&lt;/code&gt;, &lt;code&gt;--no-rebase&lt;/code&gt;, &lt;code&gt;--no-ff&lt;/code&gt; or &lt;code&gt;--rebase&lt;/code&gt; depending on which strategy you prefer&lt;/li&gt;
&lt;li&gt;As the working branch may have changed, it then &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L66-L68"&gt;re-adds any changed files&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Before &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L70-L88"&gt;creating a commit&lt;/a&gt; using the &lt;code&gt;author_name&lt;/code&gt; provided and the &lt;code&gt;--signoff&lt;/code&gt; flag if the &lt;code&gt;signoff&lt;/code&gt; input was set&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;tag&lt;/code&gt; input was provided, the action also &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L90-L102"&gt;creates a tag&lt;/a&gt; named after the &lt;code&gt;tag&lt;/code&gt; input&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;push&lt;/code&gt; input was &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L109-L120"&gt;set to true&lt;/a&gt;, it runs &lt;code&gt;git push origin &amp;lt;branch&amp;gt; --set-upstream&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;push&lt;/code&gt; input provided &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L120-L131"&gt;is a string&lt;/a&gt; it runs &lt;code&gt;git push &amp;lt;input string&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Finally, if a &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L133-L154"&gt;tag was created&lt;/a&gt; it runs &lt;code&gt;git push --tags&lt;/code&gt;. This part is interesting as it &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L138"&gt;deletes a remote tag and re-pushes&lt;/a&gt; when an error is encountered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a couple of other interesting things in the action to dig into.&lt;/p&gt;

&lt;p&gt;One of the big questions in the Actions world is how to parse structured input. &lt;code&gt;add-and-commit&lt;/code&gt; &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L353-L380"&gt;attempts to parse inputs that can be arrays as both JSON and YAML&lt;/a&gt; before falling back to treating it as a simple string.&lt;/p&gt;

&lt;p&gt;The action also &lt;a href="https://github.com/EndBug/add-and-commit/blob/51d91f68e3700e6fd488196992e790664c51f56b/src/main.ts#L217-L221"&gt;handles setting defaults in the code&lt;/a&gt; rather than using the &lt;code&gt;default&lt;/code&gt; field in &lt;code&gt;action.yml&lt;/code&gt; like &lt;code&gt;git-auto-commit-action&lt;/code&gt; &lt;a href="https://github.com/stefanzweifel/git-auto-commit-action/blob/master/action.yml#L38"&gt;does&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common use cases
&lt;/h2&gt;

&lt;p&gt;Automatically committing changed files can be useful in a couple of situations. The example shown in the docs is to run &lt;code&gt;eslint --fix&lt;/code&gt; to fix code styling before pushing the changes back to the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint source code&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint with ESLint&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&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 install&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;Update source code&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;eslint "src/**" --fix&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;Commit changes&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;EndBug/add-and-commit@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;author_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Your Name&lt;/span&gt;
          &lt;span class="na"&gt;author_email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mail@example.com&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;message"&lt;/span&gt;
          &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another idea is to make sure that your &lt;code&gt;lib&lt;/code&gt; folder for your action (like the one used in &lt;code&gt;add-and-commit&lt;/code&gt;) is always compiled as expected on push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Automatic Compile&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;push&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;run&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;Compile&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;12.x&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 install&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;Compile&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;npx ncc -o lib&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;Commit changes&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;EndBug/add-and-commit@v7&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;author_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Your Name&lt;/span&gt;
          &lt;span class="na"&gt;author_email&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mail@example.com&lt;/span&gt;
          &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;message"&lt;/span&gt;
          &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lib"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Useful links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/marketplace/actions/add-commit"&gt;GitHub Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/EndBug/add-and-commit"&gt;GitHub Repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
