<?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: Shunsuke Suzuki</title>
    <description>The latest articles on DEV Community by Shunsuke Suzuki (@suzukishunsuke).</description>
    <link>https://dev.to/suzukishunsuke</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%2F509740%2F5713b5c3-9279-4851-b12a-076404f8583f.png</url>
      <title>DEV Community: Shunsuke Suzuki</title>
      <link>https://dev.to/suzukishunsuke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/suzukishunsuke"/>
    <language>en</language>
    <item>
      <title>Short-lived GitHub Access Token for secure local development by ghtkn</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Sat, 13 Sep 2025 23:56:36 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/introducing-ghtkn-your-safer-github-token-solution-347d</link>
      <guid>https://dev.to/suzukishunsuke/introducing-ghtkn-your-safer-github-token-solution-347d</guid>
      <description>&lt;p&gt;Are you still relying on long-lived GitHub tokens—like Personal Access Tokens (PATs) or OAuth tokens used with the GitHub CLI—stored on your local machine?&lt;br&gt;
If so, you might be exposing yourself to unnecessary risks.&lt;br&gt;
&lt;a href="https://github.com/suzuki-shunsuke/ghtkn" rel="noopener noreferrer"&gt;Enter &lt;strong&gt;ghtkn&lt;/strong&gt;: a tool built to dramatically reduce those risks by using short-lived, secure tokens via GitHub Apps.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Traditional Tokens
&lt;/h2&gt;

&lt;p&gt;Many developers use PATs or GitHub CLI tokens that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remain dangerous if leaked

&lt;ul&gt;
&lt;li&gt;Live &lt;em&gt;indefinitely&lt;/em&gt; or for many months&lt;/li&gt;
&lt;li&gt;Have overly broad permissions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Are hard to rotate regularly&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Any leak or compromise can lead to serious, lasting damage.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ghtkn Solves This
&lt;/h2&gt;

&lt;p&gt;ghtkn is designed to tackle these issues by introducing a more secure, manageable workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short-lived tokens&lt;/strong&gt; — tokens expire after 8 hours, so leaks are less damaging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No secrets required&lt;/strong&gt; — you only need the GitHub App &lt;em&gt;Client ID&lt;/em&gt;, which is not sensitive. No Private Key / Client Secret to guard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-attributed actions&lt;/strong&gt; — actions are done &lt;em&gt;as you&lt;/em&gt;, not under a generic app identity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic management&lt;/strong&gt; — tokens are stored and reused securely via OS secret managers (Windows Credential Manager, macOS Keychain, GNOME Keyring). You don’t have to juggle them manually.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Here’s how you set up ghtkn:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghtkn/blob/main/INSTALL.md" rel="noopener noreferrer"&gt;&lt;strong&gt;Install&lt;/strong&gt; ghtkn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a GitHub App&lt;/strong&gt; with Device Flow enabled. Don’t bother with private keys or secrets. You only need a Client ID.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialize configuration&lt;/strong&gt; via &lt;code&gt;ghtkn init&lt;/code&gt;, to create a &lt;code&gt;ghtkn.yaml&lt;/code&gt;, where you define what GitHub Apps you’ll use.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;ghtkn get&lt;/code&gt; to generate your 8-hour user access token (via Device Flow). Approve via browser, use the code shown in terminal.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Extras &amp;amp; Advanced Use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghtkn#wrapping-commands" rel="noopener noreferrer"&gt;&lt;strong&gt;Wrapping command&lt;/strong&gt;: You can wrap commands like &lt;code&gt;gh&lt;/code&gt; (GitHub CLI) so that an access token is automatically passed via &lt;code&gt;ghtkn&lt;/code&gt;, making the process seamless.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghtkn#git-credential-helper" rel="noopener noreferrer"&gt;&lt;strong&gt;Git Credential Helper&lt;/strong&gt;: ghtkn can act as a credential helper for Git, so commands like &lt;code&gt;git push&lt;/code&gt; or &lt;code&gt;git clone&lt;/code&gt; can use it automatically.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghtkn#using-multiple-apps" rel="noopener noreferrer"&gt;&lt;strong&gt;Multiple GitHub Apps&lt;/strong&gt;: You can configure more than one App in your config, selecting which to use. Helpful for distinguishing permissions per repository.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghtkn#access-token-regeneration" rel="noopener noreferrer"&gt;&lt;strong&gt;Minimum expiration threshold&lt;/strong&gt;: You can tell &lt;code&gt;ghtkn get&lt;/code&gt; to regenerate tokens if the remaining time is less than a threshold you specify. Avoids using almost-expired tokens in long-running operations.&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Who Should Use ghtkn
&lt;/h2&gt;

&lt;p&gt;ghtkn is especially compelling for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers who want to minimize their risk exposure from token leaks.&lt;/li&gt;
&lt;li&gt;Anyone who uses GitHub CLI or other tools locally and needs safe, manageable authentication.&lt;/li&gt;
&lt;li&gt;Teams where multiple people or organizations share repos but want fine control over permissions.&lt;/li&gt;
&lt;li&gt;Organizations wanting to standardize token policies internally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why ghtkn Might Be a Game Changer
&lt;/h2&gt;

&lt;p&gt;By shifting away from long-lived, broad-scope tokens to short-lived, narrowly scoped user-attributed tokens, ghtkn helps you follow security best practices effortlessly.&lt;br&gt;
It sidesteps many of the most common security pitfalls—like stale credentials or broad permissions—while integrating nicely into existing workflows.&lt;/p&gt;

&lt;p&gt;If you care at all about securing your GitHub workflows—and especially if you work in teams or public projects—ghtkn is worth a look.&lt;br&gt;
Give it a try, and make your access tokens safer by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghtkn" rel="noopener noreferrer"&gt;https://github.com/suzuki-shunsuke/ghtkn&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>github</category>
      <category>cli</category>
    </item>
    <item>
      <title>How to pin GitHub Actions to a full commit length SHA across all repositories in your organization</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Mon, 17 Mar 2025 07:30:59 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/pin-github-actions-to-a-full-length-commit-sha-for-security-2n7p</link>
      <guid>https://dev.to/suzukishunsuke/pin-github-actions-to-a-full-length-commit-sha-for-security-2n7p</guid>
      <description>&lt;p&gt;Last weekend, the popular GitHub Action &lt;a href="https://github.com/tj-actions/changed-files" rel="noopener noreferrer"&gt;tj-actions/changed-files&lt;/a&gt; was compromised.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://semgrep.dev/blog/2025/popular-github-action-tj-actionschanged-files-is-compromised/" rel="noopener noreferrer"&gt;https://semgrep.dev/blog/2025/popular-github-action-tj-actionschanged-files-is-compromised/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised" rel="noopener noreferrer"&gt;https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/tj-actions/changed-files/issues/2463#issuecomment-2727015784" rel="noopener noreferrer"&gt;The issue was solved&lt;/a&gt;, but similar incidents could happen again in the future.&lt;br&gt;
To prevent such issues, pinning action versions by full commit hash is recommended.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions" rel="noopener noreferrer"&gt;GitHub Docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pin actions to a full length commit SHA&lt;br&gt;
Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release.&lt;br&gt;
Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.&lt;br&gt;
When selecting a SHA, you should verify it is from the action's repository and not a repository fork.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post introduces how to pin GitHub Action versions across all repositories in your organization.&lt;/p&gt;
&lt;h2&gt;
  
  
  pinact
&lt;/h2&gt;

&lt;p&gt;You can pin GitHub Actions using a CLI tool called &lt;a href="https://github.com/suzuki-shunsuke/pinact" rel="noopener noreferrer"&gt;pinact&lt;/a&gt;.&lt;br&gt;
It's so easy to use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;If you want to fix composite actions and documents, you can specify file paths.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pinact run action.yaml README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also update actions using the &lt;code&gt;-u&lt;/code&gt; option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pinact run &lt;span class="nt"&gt;-u&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more details, check out &lt;a href="https://github.com/suzuki-shunsuke/pinact" rel="noopener noreferrer"&gt;the pinact repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  multi-gitter
&lt;/h2&gt;

&lt;p&gt;You can apply pinact to all repositories using &lt;a href="https://github.com/lindell/multi-gitter" rel="noopener noreferrer"&gt;multi-gitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;config.yaml
run.sh
main.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;config.yaml:&lt;/p&gt;

&lt;p&gt;e.g.&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;git-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cmd&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;pin-github-actions&lt;/span&gt;
&lt;span class="na"&gt;skip-forks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;org&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;szksh-lab&lt;/span&gt; &lt;span class="c1"&gt;# Update with your organization&lt;/span&gt;
&lt;span class="c1"&gt;# repo:&lt;/span&gt;
&lt;span class="c1"&gt;#   - szksh-lab/test-1 # For test&lt;/span&gt;
&lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;pin-github-actions&lt;/span&gt;
&lt;span class="na"&gt;pr-title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ci:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Pin&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;versions&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;GitHub&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Actions&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;to&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;full&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;hash"&lt;/span&gt;
&lt;span class="na"&gt;pr-body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
  &lt;span class="s"&gt;## What?&lt;/span&gt;

  &lt;span class="s"&gt;This PR pins versions of GitHub Actions to full commit hash by pinact.&lt;/span&gt;
  &lt;span class="s"&gt;In general, this PR doesn't change the behavior of the workflows, so you can merge this safely.&lt;/span&gt;

  &lt;span class="s"&gt;## Why?&lt;/span&gt;

  &lt;span class="s"&gt;Last weekend, the popular GitHub Action tj-actions/changed-files action was compromised.&lt;/span&gt;

  &lt;span class="s"&gt;- https://semgrep.dev/blog/2025/popular-github-action-tj-actionschanged-files-is-compromised/&lt;/span&gt;
  &lt;span class="s"&gt;- https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised&lt;/span&gt;

  &lt;span class="s"&gt;All tags were tampered and they pointed to a revision with malicious code.&lt;/span&gt;

  &lt;span class="s"&gt;The issue was solved, but similar issues could happen again.&lt;/span&gt;
  &lt;span class="s"&gt;To avoid this kind of issues, we should pin versions of GitHub Actions.&lt;/span&gt;
  &lt;span class="s"&gt;This is a common practice of GitHub Actions.&lt;/span&gt;

  &lt;span class="s"&gt;https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-third-party-actions&lt;/span&gt;

  &lt;span class="s"&gt;&amp;gt; Pin actions to a full length commit SHA&lt;/span&gt;
  &lt;span class="s"&gt;&amp;gt; Pinning an action to a full length commit SHA is currently the only way to use an action as an immutable release.&lt;/span&gt;
  &lt;span class="s"&gt;&amp;gt; Pinning to a particular SHA helps mitigate the risk of a bad actor adding a backdoor to the action's repository, as they would need to generate a SHA-1 collision for a valid Git object payload.&lt;/span&gt;
  &lt;span class="s"&gt;&amp;gt; When selecting a SHA, you should verify it is from the action's repository and not a repository fork.&lt;/span&gt;

  &lt;span class="s"&gt;## How was this pull request created?&lt;/span&gt;

  &lt;span class="s"&gt;This pull request was created by [pinact](https://github.com/suzuki-shunsuke/pinact) and [multi-gitter](https://github.com/lindell/multi-gitter).&lt;/span&gt;

  &lt;span class="s"&gt;## Due Date&lt;/span&gt;

  &lt;span class="s"&gt;Please merge this pull request by 2025-03-31.&lt;/span&gt;
  &lt;span class="s"&gt;If it's difficult, please ask at the Slack channel #sre.&lt;/span&gt;

  &lt;span class="s"&gt;## Contact&lt;/span&gt;

  &lt;span class="s"&gt;If you have any question, please ask at the Slack channel #sre.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;run.sh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eu&lt;/span&gt;

&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh auth token&lt;span class="si"&gt;)&lt;/span&gt;

multi-gitter run ./main.sh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--config&lt;/span&gt; config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;main.sh:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-eu&lt;/span&gt;

pinact run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make main.sh executable:&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;chmod&lt;/span&gt; +x main.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before running it across all repositories, test it on one repository:&lt;/p&gt;

&lt;p&gt;config.yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# org:&lt;/span&gt;
&lt;span class="c1"&gt;#   - szksh-lab&lt;/span&gt;
&lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;szksh-lab/test-1&lt;/span&gt; &lt;span class="c1"&gt;# Please fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;run.sh&lt;/code&gt; to create a pull request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once confirmed, apply it to all repositories.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash run.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example Pull Request: &lt;a href="https://github.com/szksh-lab/test-1/pull/23" rel="noopener noreferrer"&gt;https://github.com/szksh-lab/test-1/pull/23&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For more details of multi-gitter, please see &lt;a href="https://github.com/lindell/multi-gitter" rel="noopener noreferrer"&gt;the multi-gitter repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuous Updates
&lt;/h2&gt;

&lt;p&gt;You can update actions by &lt;a href="https://docs.github.com/en/code-security/dependabot/working-with-dependabot" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt; or &lt;a href="https://docs.renovatebot.com/" rel="noopener noreferrer"&gt;Renovate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Dependabot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.blog/news-insights/product-news/dependabot-now-updates-your-actions-workflows/" rel="noopener noreferrer"&gt;https://github.blog/news-insights/product-news/dependabot-now-updates-your-actions-workflows/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Renovate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.renovatebot.com/presets-helpers/#helperspingithubactiondigeststosemver" rel="noopener noreferrer"&gt;helpers:pinGitHubActionDigestsToSemver&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also run pinact in CI using &lt;a href="https://github.com/suzuki-shunsuke/pinact-action" rel="noopener noreferrer"&gt;pinact-action&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;To improve security, you should pin GitHub Action versions to a full-length commit hash.&lt;br&gt;
You can pin GitHub Actions across all repositories in your GitHub Organizations using pinact and multi-gitter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/pinact" rel="noopener noreferrer"&gt;pinact&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lindell/multi-gitter" rel="noopener noreferrer"&gt;multi-gitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Fix Code Via GitHub Actions By Verified Commits</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Sat, 15 Feb 2025 03:45:50 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/fix-code-via-github-actions-by-verified-commits-3o1d</link>
      <guid>https://dev.to/suzukishunsuke/fix-code-via-github-actions-by-verified-commits-3o1d</guid>
      <description>&lt;p&gt;This post introduces a GitHub Action to fix code by &lt;strong&gt;verified commits&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/suzuki-shunsuke/commit-action" rel="noopener noreferrer"&gt;https://github.com/suzuki-shunsuke/commit-action&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;It's useful to fix pull requests via GitHub Actions.&lt;br&gt;
Or it's also useful to fix code on a base branch after merging pull requests.&lt;br&gt;
For instance, you can format code, and generate document from source codes automatically.&lt;/p&gt;

&lt;p&gt;To achieve this, you need to create and push commits in CI.&lt;br&gt;
&lt;a href="https://github.com/suzuki-shunsuke/commit-action" rel="noopener noreferrer"&gt;commit-action&lt;/a&gt; is an action for this.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Use commit-action?
&lt;/h2&gt;

&lt;p&gt;Unlike similar actions, &lt;strong&gt;commit-action creates and pushes commits by GitHub API instead of Git commands&lt;/strong&gt;.&lt;br&gt;
So you can create &lt;strong&gt;verified commits&lt;/strong&gt; using GitHub Actions token &lt;code&gt;${{github.token}}&lt;/code&gt; or a GitHub App installation access token.&lt;/p&gt;

&lt;p&gt;Commit signing is so important for security.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/authentication/managing-commit-signature-verification" rel="noopener noreferrer"&gt;https://docs.github.com/en/authentication/managing-commit-signature-verification&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create verified commits using Git, a GPG key or SSH key is required.&lt;br&gt;
It's bothersome to manage GPG keys and SSH keys properly for automation, so it's awesome that commit-action can create verified commits without them.&lt;/p&gt;
&lt;h2&gt;
  
  
  How To Use
&lt;/h2&gt;

&lt;p&gt;commit-action is so easy to use.&lt;br&gt;
All inputs are optional.&lt;/p&gt;

&lt;p&gt;You only need to run commit-action after fixing code in workflows.&lt;br&gt;
Then it creates and pushes a commit to a remote branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Example&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;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&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;example&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-24.04&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@11bd71901bbe5b1630ceea73d27597364c9af683&lt;/span&gt; &lt;span class="c1"&gt;# v4.2.2&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;persist-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

      &lt;span class="c1"&gt;# Fix files&lt;/span&gt;
      &lt;span class="c1"&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;Push changes to the remote branch&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;suzuki-shunsuke/commit-action@db754eb4adb44fb5aee5879a3bd08785efec198e&lt;/span&gt; &lt;span class="c1"&gt;# v0.0.4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;commit-action fails if it pushes a commit.&lt;br&gt;
If no change is pushed, commit-action does nothing and exits successfully.&lt;/p&gt;

&lt;p&gt;By default, commit-action pushes a commit to &lt;code&gt;${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}&lt;/code&gt; in &lt;code&gt;$GITHUB_REPOSITORY&lt;/code&gt;, but you can change them.&lt;br&gt;
&lt;code&gt;${{github.token}}&lt;/code&gt; is used by default, but we don't recommend it because &lt;code&gt;${{github.token}}&lt;/code&gt; doesn't trigger a new workflow run.&lt;br&gt;
We recommend GitHub App installation access tokens.&lt;br&gt;
You can create a GitHub App installation access token and pass it to commit-action yourself, but you can also pass a pair of GitHub App ID and private key.&lt;br&gt;
Then commit-action creates a GitHub App installation access token with minimum &lt;code&gt;repositories&lt;/code&gt; and &lt;code&gt;permissions&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="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;suzuki-shunsuke/commit-action@db754eb4adb44fb5aee5879a3bd08785efec198e&lt;/span&gt; &lt;span class="c1"&gt;# v0.0.4&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;app_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.APP_ID}}&lt;/span&gt;
    &lt;span class="na"&gt;app_private_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{secrets.APP_PRIVATE_KEY}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;commit-action commits all created, updated, and deleted files by default, but you can also commit only specific files.&lt;br&gt;
And you can also change the commit message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;suzuki-shunsuke/commit-action@db754eb4adb44fb5aee5879a3bd08785efec198e&lt;/span&gt; &lt;span class="c1"&gt;# v0.0.4&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;commit_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;style:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;format&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;code"&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;README.md&lt;/span&gt;
      &lt;span class="s"&gt;package-lock.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Refactor Terraform Resource Names By One Command</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Mon, 23 Dec 2024 14:55:15 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/refactor-terraform-resource-names-by-one-command-4amh</link>
      <guid>https://dev.to/suzukishunsuke/refactor-terraform-resource-names-by-one-command-4amh</guid>
      <description>&lt;p&gt;This post introduces &lt;a href="https://github.com/suzuki-shunsuke/tfmv" rel="noopener noreferrer"&gt;tfmv, a CLI tool that simplifies refactoring Terraform resource names with just one command&lt;/a&gt;.&lt;br&gt;
For instance, to replace hyphens (&lt;code&gt;-&lt;/code&gt;) with underscores (&lt;code&gt;_&lt;/code&gt;), you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tfmv &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'-/_'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then resource are renamed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-resource "github_repository" "example-1" {
&lt;/span&gt;&lt;span class="gi"&gt;+resource "github_repository" "example_1" {
&lt;/span&gt;   name = "example-1"
 }
&lt;span class="err"&gt;
&lt;/span&gt; data "github_branch" "example" {
&lt;span class="gd"&gt;-  repository = github_repository.example-1.name
&lt;/span&gt;&lt;span class="gi"&gt;+  repository = github_repository.example_1.name
&lt;/span&gt;   branch     = "example"
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a moved block is created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;moved&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;from&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="nx"&gt;example-1&lt;/span&gt;
  &lt;span class="nx"&gt;to&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="nx"&gt;example_1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Features of tfmv
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Rename Terraform Resources, Data Sources, and Modules&lt;/li&gt;
&lt;li&gt;Modify resource references&lt;/li&gt;
&lt;li&gt;Generate moved blocks&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/suzuki-shunsuke/tfmv/blob/main/docs/install.md" rel="noopener noreferrer"&gt;Easy to install&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;tfmv is a single binary written in Go. You only need to put tfmv into $PATH&lt;/li&gt;
&lt;li&gt;Cross platform: Windows, macOS, and Linux&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Flexible Resource Filtering with Regular Expressions

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;--include &amp;lt;regexp&amp;gt;&lt;/code&gt; to rename only resources matching a specific pattern&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;--exclude &amp;lt;regexp&amp;gt;&lt;/code&gt; to skip renaming for resources matching a specific pattern&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Support for Multiple Renaming Methods

&lt;ul&gt;
&lt;li&gt;Fixed String Replacement: Use &lt;code&gt;--replace (-r)&lt;/code&gt; to specify straightforward string substitutions&lt;/li&gt;
&lt;li&gt;Regular Expression: Use &lt;code&gt;--regexp&lt;/code&gt; to perform pattern-based renaming&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jsonnet.org" rel="noopener noreferrer"&gt;Jsonnet&lt;/a&gt;: Use &lt;code&gt;--jsonnet (-j)&lt;/code&gt; for advanced, programmatically controlled renaming logic&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These features make tfmv a powerful and flexible tool for refactoring Terraform configurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/tfmv/blob/main/docs/install.md" rel="noopener noreferrer"&gt;Install tfmv according to the guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;main.tf&lt;/code&gt; and make a backup of it as &lt;code&gt;main.tf.bak&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'resource "github_repository" "example-1" {
  name = "example-1"
}

data "github_branch" "example-2" {
  repository = github_repository.example-1.name
  branch     = "example"
}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; main.tf
&lt;span class="nb"&gt;cp &lt;/span&gt;main.tf main.tf.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run tfmv to replace &lt;code&gt;-&lt;/code&gt; with &lt;code&gt;_&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tfmv &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'-/_'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then check the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff main.tf main.tf.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;1c1
&lt;/span&gt;&lt;span class="gd"&gt;&amp;lt; resource "github_repository" "example_1" {
&lt;/span&gt;&lt;span class="p"&gt;---
&lt;/span&gt;&lt;span class="gi"&gt;&amp;gt; resource "github_repository" "example-1" {
&lt;/span&gt;&lt;span class="p"&gt;5,6c5,6
&lt;/span&gt;&lt;span class="gd"&gt;&amp;lt; data "github_branch" "example_2" {
&amp;lt;   repository = github_repository.example_1.name
&lt;/span&gt;&lt;span class="p"&gt;---
&lt;/span&gt;&lt;span class="gi"&gt;&amp;gt; data "github_branch" "example-2" {
&amp;gt;   repository = github_repository.example-1.name
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;moved.tf:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;moved&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;from&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="nx"&gt;example-1&lt;/span&gt;
  &lt;span class="nx"&gt;to&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="nx"&gt;example_1&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! 🎉 You’ve successfully refactored Terraform resource names with just one command.&lt;/p&gt;

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

&lt;p&gt;In this post I introduced tfmv, a powerful Terraform refactoring tool.&lt;br&gt;
Using tfmv, you can efficiently rename Terraform resources and generate moved blocks.&lt;br&gt;
&lt;a href="https://github.com/suzuki-shunsuke/tfmv" rel="noopener noreferrer"&gt;For more details, check out the README.md on GitHub.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
    </item>
    <item>
      <title>Lock Mechanism on GitHub Actions</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Thu, 14 Nov 2024 13:41:31 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/lock-mechanism-on-github-actions-265i</link>
      <guid>https://dev.to/suzukishunsuke/lock-mechanism-on-github-actions-265i</guid>
      <description>&lt;p&gt;Sometimes, a lock mechanism is useful in CI workflows, like when you need to prevent simultaneous deployments or block deployments during maintenance.&lt;br&gt;
In this post, I’ll introduce a GitHub Action for implementing a lock mechanism.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/suzuki-shunsuke/lock-action" rel="noopener noreferrer"&gt;https://github.com/suzuki-shunsuke/lock-action&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No dependencies on external services like AWS or GCP&lt;/li&gt;
&lt;li&gt;No reliance on shell or external commands such as git&lt;/li&gt;
&lt;li&gt;Achieves locking using GitHub branches&lt;/li&gt;
&lt;li&gt;Manage branches via GitHub API without git command. You don't have to checkout repositories by &lt;a href="https://github.com/actions/checkout" rel="noopener noreferrer"&gt;actions/checkout&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Records lock and unlock histories&lt;/li&gt;
&lt;li&gt;Supports waiting until a lock is released&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How to Use
&lt;/h2&gt;

&lt;p&gt;This action requires two inputs: &lt;code&gt;key&lt;/code&gt; and &lt;code&gt;mode&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;key&lt;/code&gt;: an explicit identifier for the lock, which you can adjust by service and environment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mode&lt;/code&gt;: the action’s operational mode, which can be one of these:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lock&lt;/code&gt;: Acquires a lock&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unlock&lt;/code&gt;: Releases a lock&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;check&lt;/code&gt;: Checks the lock status&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;mode: lock&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;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;Acquire a lock for a key `foo` before deploying an application&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;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lock&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo&lt;/span&gt;
  &lt;span class="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="s"&gt;bash deploy.sh foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mode: unlock&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;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;Release a lock&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;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unlock&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mode: check&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;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check if a key is being locked&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;check&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;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo&lt;/span&gt;
  &lt;span class="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="s"&gt;bash deploy.sh foo&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps.check.outputs.already_locked != 'true'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use &lt;code&gt;post_unlock: "true"&lt;/code&gt; to release a lock automatically in a post step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lock&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo&lt;/span&gt;
      &lt;span class="na"&gt;post_unlock&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, &lt;code&gt;mode: lock&lt;/code&gt; will fail if the key is already locked.&lt;br&gt;
Set &lt;code&gt;ignore_already_locked_error: "true"&lt;/code&gt; to avoid this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lock&lt;/span&gt;
    &lt;span class="na"&gt;ignore_already_locked_error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To force &lt;code&gt;mode: check&lt;/code&gt; to fail if a key is locked, use &lt;code&gt;fail_if_locked: "true"&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="c1"&gt;# This step fails if the key `foo` is being locked.&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;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;foo&lt;/span&gt;
    &lt;span class="na"&gt;fail_if_locked&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To wait until a lock is released, use &lt;code&gt;max_wait_seconds&lt;/code&gt; and &lt;code&gt;wait_interval_seconds&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="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;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lock&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="c1"&gt;# Try to acquire a lock every 10 seconds until acquiring a lock or 60 seconds pass.&lt;/span&gt;
    &lt;span class="na"&gt;max_wait_seconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;60"&lt;/span&gt;
    &lt;span class="na"&gt;wait_interval_seconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These inputs are also available for &lt;code&gt;mode: check&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="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;suzuki-shunsuke/lock-action@latest&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="c1"&gt;# Check a lock every 5 seconds until the lock is released or 60 seconds pass&lt;/span&gt;
    &lt;span class="na"&gt;max_wait_seconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;30"&lt;/span&gt;
    &lt;span class="na"&gt;wait_interval_seconds&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;This action manages locks by creating and updating GitHub branches.&lt;br&gt;
Each lock’s state is tracked in the commit message of a branch named &lt;code&gt;${{inputs.key_prefix}}${{inputs.key}}&lt;/code&gt; (default prefix: &lt;code&gt;lock__&lt;/code&gt;, which can be customized with &lt;code&gt;key_prefix&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Commit message format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unlock by suzuki-shunsuke: test
{
  "message": "test",
  "state": "unlock",
  "actor": "suzuki-shunsuke",
  "github_actions_workflow_run_url": "https://github.com/suzuki-shunsuke/test-github-action/actions/runs/11545637203?pr=237",
  "pull_request_number": 237
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From these commit messages, you can see when and who (actor, workflow run, pull request number) acquired or released the lock.&lt;/p&gt;

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

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

&lt;p&gt;Example links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/lock-action/branches/all?query=lock__&amp;amp;lastTab=overview" rel="noopener noreferrer"&gt;Branch list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/suzuki-shunsuke/lock-action/commits/lock__test-1/" rel="noopener noreferrer"&gt;Commit history for a specific lock&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;In this post, I introduced my GitHub Action for a lock mechanism. For more details, please visit the repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/suzuki-shunsuke/lock-action" rel="noopener noreferrer"&gt;https://github.com/suzuki-shunsuke/lock-action&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>aqua Now Supports Node.js</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Sun, 01 Sep 2024 08:05:27 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/aqua-now-supports-nodejs-2od0</link>
      <guid>https://dev.to/suzukishunsuke/aqua-now-supports-nodejs-2od0</guid>
      <description>&lt;p&gt;aqua is a CLI version manager written in Go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aquaproj.github.io/" rel="noopener noreferrer"&gt;https://aquaproj.github.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As of August 24, 2024, aqua now supports Node.js!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aquaproj.github.io/docs/reference/nodejs-support/" rel="noopener noreferrer"&gt;https://aquaproj.github.io/docs/reference/nodejs-support/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Manage Node.js Using aqua
&lt;/h2&gt;

&lt;p&gt;You'll need &lt;a href="https://github.com/aquaproj/aqua-registry/releases/tag/v4.216.0" rel="noopener noreferrer"&gt;aqua-registry v4.216.0&lt;/a&gt; or later.&lt;br&gt;
To get started, add nodejs/node to your aqua.yaml file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aqua g &lt;span class="nt"&gt;-i&lt;/span&gt; nodejs/node
aqua i &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can run commands such as &lt;code&gt;node&lt;/code&gt; and &lt;code&gt;npm&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
npm &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure npm &lt;code&gt;preset&lt;/code&gt; config
&lt;/h2&gt;

&lt;p&gt;To install tools globally using &lt;code&gt;npm i -g&lt;/code&gt;, you need to configure the npm &lt;code&gt;preset&lt;/code&gt; config and update the &lt;code&gt;PATH&lt;/code&gt; environment variable.&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;export &lt;/span&gt;&lt;span class="nv"&gt;NPM_CONFIG_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;XDG_DATA_HOME&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="p"&gt;/.local/share&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/npm-global &lt;span class="c"&gt;# Feel free to change this path&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$NPM_CONFIG_PREFIX&lt;/span&gt;/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once configured, you can install tools by &lt;code&gt;npm i -g&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; zx
zx &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if you change the version of Node.js, you'll still be able to execute tools installed by &lt;code&gt;npm i -g&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aqua up node@v20.16.0
zx &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why do we need to configure npm &lt;code&gt;preset&lt;/code&gt; config?
&lt;/h2&gt;

&lt;p&gt;For more details, check out the related &lt;a href="https://github.com/aquaproj/aqua/issues/2996#issuecomment-2304011654" rel="noopener noreferrer"&gt;issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By default, &lt;code&gt;npm i -g&lt;/code&gt; installs tools in the same directory as Node.js, meaning the installation path depends on the Node.js version.&lt;br&gt;
Currently, aqua can't dynamically update the &lt;code&gt;PATH&lt;/code&gt; environment variable.&lt;br&gt;
We've considered dynamically updating the &lt;code&gt;PATH&lt;/code&gt; environment variable for Node.js, but this feature is highly dependent on the environment (shell), making it difficult to maintain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash: $PROMPT_COMMAND&lt;/li&gt;
&lt;li&gt;Zsh: hook function&lt;/li&gt;
&lt;li&gt;Fish: ???&lt;/li&gt;
&lt;li&gt;Powershell: ???&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We prefer not to implement such a feature.&lt;/p&gt;

&lt;p&gt;Moreover, dynamically updating the environment variable works only on interactive shells, not in shell scripts.&lt;br&gt;
For example, changing the Node.js version within a shell script wouldn’t update the PATH variable correctly.&lt;/p&gt;

&lt;p&gt;Therefore, we decided to fix the installation directory using npm &lt;code&gt;preset&lt;/code&gt; config rather than updating dynamically the &lt;code&gt;PATH&lt;/code&gt; environment variable.&lt;br&gt;
This approach has several benefits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It doesn't depend on the environment (shell)&lt;/li&gt;
&lt;li&gt;It doesn't require adding any feature to aqua itself&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Gathering GitHub Issues and Pull Requests Across Repositories into GitHub Projects Automatically</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Sun, 04 Aug 2024 13:21:45 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/pull-together-github-issues-and-pull-requests-across-repositories-to-github-projects-automatically-a87</link>
      <guid>https://dev.to/suzukishunsuke/pull-together-github-issues-and-pull-requests-across-repositories-to-github-projects-automatically-a87</guid>
      <description>&lt;p&gt;This post explains how to gather GitHub Issues and Pull Requests across GitHub repositories into GitHub Projects automatically.&lt;br&gt;
This post is based on my post written in Japanese. &lt;a href="https://zenn.dev/shunsuke_suzuki/articles/add-github-issue-pr-to-project" rel="noopener noreferrer"&gt;自分が管理する全 OSS の Issue や Pull Request を 1 つの GitHub Project に集約&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Managing GitHub Issues and Pull Requests across multiple repositories can be challenging.&lt;br&gt;
By adding all of them to GitHub Projects, you can manage them in one place.&lt;br&gt;
To do this, you need to add them to GitHub Projects somehow.&lt;/p&gt;

&lt;p&gt;There are several ways to add them to GitHub Projects automatically.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/using-the-built-in-automations" rel="noopener noreferrer"&gt;Using the built-in automations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions" rel="noopener noreferrer"&gt;Running GitHub Actions by issues and pull requests' opened events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;(Recommendation) Running GitHub Actions by schedule event to search issues and pull requests and add them to projects&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I recommend the third approach and will describe it in detail.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Using the built-in automations
&lt;/h2&gt;

&lt;p&gt;GitHub provides the built-in automations, but this feature has several limitations.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have to configure workflows for each repository, which is cumbersome&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fzenn-user-upload%2F280cb65d9348-20240713.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fzenn-user-upload%2F280cb65d9348-20240713.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can create only five auto-add workflows. To create more workflows, you have to upgrade your plan&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fzenn-user-upload%2F64dcd54dc14a-20240713.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstorage.googleapis.com%2Fzenn-user-upload%2F64dcd54dc14a-20240713.png"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Running GitHub Actions by issues and pull requests' opened events
&lt;/h2&gt;

&lt;p&gt;You can add issues and pull requests to projects using GitHub Actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions" rel="noopener noreferrer"&gt;https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/automating-projects-using-actions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub provides an official action for this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/marketplace/actions/add-to-github-projects" rel="noopener noreferrer"&gt;https://github.com/marketplace/actions/add-to-github-projects&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, I don't recommend this approach because you have to add workflows for each repository, which makes it hard to maintain.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Running GitHub Actions by schedule event to search issues and pull requests and add them to projects
&lt;/h2&gt;

&lt;p&gt;You can run GitHub Actions by schedule event to search issues and pull requests and add them to projects.&lt;br&gt;
The benefit of this approach is that you only have to maintain a single workflow.&lt;/p&gt;

&lt;p&gt;I've developed a command-line tool for this purpose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghproj" rel="noopener noreferrer"&gt;https://github.com/suzuki-shunsuke/ghproj&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tool performs the following tasks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Reads a configuration file&lt;/li&gt;
&lt;li&gt;Searches issues and pull requests using GitHub GraphQL API&lt;/li&gt;
&lt;li&gt;Excludes some issues and pull requests based on an expression&lt;/li&gt;
&lt;li&gt;Adds issues and pull requests to projects
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ghproj init &lt;span class="c"&gt;# Scaffold a configuration file ghproj.yaml&lt;/span&gt;
ghproj add &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-config&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;-c&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;configuration file path&amp;gt;] &lt;span class="c"&gt;# Add issues and pull requests to GitHub Projects&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here is an example of a configuration file &lt;code&gt;ghproj.yaml&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;entries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# query: The query of GitHub GraphQL API to search issues and pull requests which are added to GitHub Projects&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;is:open&lt;/span&gt;
      &lt;span class="s"&gt;-project:suzuki-shunsuke/5&lt;/span&gt;
      &lt;span class="s"&gt;archived:false&lt;/span&gt;
      &lt;span class="s"&gt;owner:suzuki-shunsuke&lt;/span&gt;
      &lt;span class="s"&gt;owner:aquaproj&lt;/span&gt;
      &lt;span class="s"&gt;owner:lintnet&lt;/span&gt;
      &lt;span class="s"&gt;is:public&lt;/span&gt;
    &lt;span class="c1"&gt;# expr: An expression filtering the search result.&lt;/span&gt;
    &lt;span class="c1"&gt;# The expression is evaluated using github.com/expr-lang/expr.&lt;/span&gt;
    &lt;span class="c1"&gt;# The expression is evaluated per item in the search result.&lt;/span&gt;
    &lt;span class="c1"&gt;# The evaluation result must be a boolean.&lt;/span&gt;
    &lt;span class="c1"&gt;# If the result is false, the item is excluded.&lt;/span&gt;
    &lt;span class="c1"&gt;# expr is optional. If expr isn't set, all search results are used.&lt;/span&gt;
    &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;(! Item.Repo.IsFork) &amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="s"&gt;(Item.Title != "Dependency Dashboard")&lt;/span&gt;
    &lt;span class="c1"&gt;# project_id: GitHub Project id where issues and pull requests are added.&lt;/span&gt;
    &lt;span class="c1"&gt;# You can get the id by GitHub CLI's gh project list command.&lt;/span&gt;
    &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PVT_kwHOAMtMJ84AQCf4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on the query syntax, please see the GitHub documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/issues/tracking-your-work-with-issues/filtering-and-searching-issues-and-pull-requests#about-search-terms" rel="noopener noreferrer"&gt;https://docs.github.com/en/issues/tracking-your-work-with-issues/filtering-and-searching-issues-and-pull-requests#about-search-terms&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can test queries using the search box on GitHub: &lt;a href="https://github.com" rel="noopener noreferrer"&gt;https://github.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F49fae165-d9b1-4be2-9bfc-ca35b1f8bde0" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F49fae165-d9b1-4be2-9bfc-ca35b1f8bde0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Access Token
&lt;/h2&gt;

&lt;p&gt;A GitHub access token is needed to add issues and pull requests to projects.&lt;br&gt;
There are several types of GitHub access tokens.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/actions/security-guides/automatic-token-authentication" rel="noopener noreferrer"&gt;GitHub Actions token&lt;/a&gt;: This token doesn't support adding items to GitHub Projects&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app" rel="noopener noreferrer"&gt;GitHub App&lt;/a&gt;: This token doesn't support managing GitHub Users' Projects&lt;/li&gt;
&lt;li&gt;OAuth App (GitHub CLI)&lt;/li&gt;
&lt;li&gt;Personal Access Token (PAT)

&lt;ul&gt;
&lt;li&gt;classic PAT: This token is insecure because you can't restrict permissions and scopes flexibly&lt;/li&gt;
&lt;li&gt;fine-grained PAT: This token doesn't support GitHub Projects&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fine-grained PAT doesn't support GitHub Projects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/orgs/community/discussions/36441" rel="noopener noreferrer"&gt;https://github.com/orgs/community/discussions/36441&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are also some APIs that do not yet support the fine-grained permission model, that we'll be adding support for in time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packages&lt;/li&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;So there are two options.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull together issues and pull requests to GitHub Users' Projects using a classic PAT&lt;/li&gt;
&lt;li&gt;(Recommendation) Pull together issues and pull requests to GitHub Organizations' Projects using a GitHub App&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;GitHub App is more secure than classic PAT, so I recommend the second option.&lt;br&gt;
Even if you want to manage your personal issues and pull requests, I recommend creating a GitHub Organization and adopting the second option.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Using a classic PAT
&lt;/h2&gt;

&lt;p&gt;You should configure the expiration date.&lt;br&gt;
The required scopes are &lt;code&gt;read:org&lt;/code&gt; and &lt;code&gt;project&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Using a GitHub App
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create a GitHub App&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The required permissions are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Repository permissions&lt;/code&gt;: &lt;code&gt;metadata: read-only&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Organization permissions&lt;/code&gt;: &lt;code&gt;Projects: Read and write&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Install the GitHub App into a repository where GitHub Actions is run&lt;/li&gt;
&lt;li&gt;Create an installation access token using an action such as &lt;a href="https://github.com/tibdex/github-app-token" rel="noopener noreferrer"&gt;tibdex/github-app-token&lt;/a&gt; or &lt;a href="https://github.com/actions/create-github-app-token" rel="noopener noreferrer"&gt;actions/create-github-app-token&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To create an installation token, you have to install the GitHub App to repositories.&lt;br&gt;
To do this, you have to grant any Repository permissions to the GitHub App.&lt;/p&gt;

&lt;p&gt;If you want to manage issues and pull requests of private repositories, you have to grant &lt;code&gt;Pull Requests: read-only&lt;/code&gt; and &lt;code&gt;Issues: read-only&lt;/code&gt; permissions to the GitHub App and install it to private repositories.&lt;/p&gt;
&lt;h2&gt;
  
  
  Archiving GitHub Projects Items Based on Conditions
&lt;/h2&gt;

&lt;p&gt;You can add only 1,200 items in each project.&lt;br&gt;
To add more items to a project, you have to archive existing items.&lt;br&gt;
GitHub Project supports workflows to archive items automatically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically" rel="noopener noreferrer"&gt;https://docs.github.com/en/issues/planning-and-tracking-with-projects/automating-your-project/archiving-items-automatically&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Archiving items will help you stay below the limit of 1,200 items in each project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This feature is useful, but sometimes you may want to archive items based on specific conditions.&lt;br&gt;
For example, you may want to archive issues and pull requests if their repositories are archived.&lt;/p&gt;

&lt;p&gt;The above tool &lt;a href="https://github.com/suzuki-shunsuke/ghproj" rel="noopener noreferrer"&gt;ghproj&lt;/a&gt; supports archiving them.&lt;/p&gt;

&lt;p&gt;e.g. ghproj.yaml&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;entries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;(Item.Open &amp;amp;&amp;amp; Item.Repo.IsArchived) ||&lt;/span&gt;
      &lt;span class="s"&gt;(Item.Title == "Dependency Dashboard")&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;archive&lt;/span&gt;
    &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PVT_kwHOAMtMJ84AQCf4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub GraphQL API can't query GitHub Project items, so ghproj retrieves all items and filter them using &lt;a href="https://github.com/expr-lang/expr" rel="noopener noreferrer"&gt;expr-lang/expr&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation with GitHub Actions
&lt;/h2&gt;

&lt;p&gt;You can gather issues and pull requests into GitHub Projects automatically by running ghproj periodically via GitHub Actions schedule events.&lt;/p&gt;

&lt;p&gt;This is the workflow I actually use. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/szksh-lab/.github/blob/main/.github/workflows/update-project.yaml" rel="noopener noreferrer"&gt;https://github.com/szksh-lab/.github/blob/main/.github/workflows/update-project.yaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This workflow pulls together all issues and pull requests to the GitHub Project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/orgs/szksh-lab/projects/1" rel="noopener noreferrer"&gt;https://github.com/orgs/szksh-lab/projects/1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sorting issues and pull requests to multiple projects
&lt;/h2&gt;

&lt;p&gt;The tool &lt;a href="https://github.com/suzuki-shunsuke/ghproj" rel="noopener noreferrer"&gt;ghproj&lt;/a&gt; supports multiple projects, so you can sort issues and pull requests to different projects.&lt;/p&gt;

&lt;p&gt;e.g. ghproj.yaml&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;entries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Add issues and pull requests with the label team/sre to the project of the SRE team&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;is:open archived:false -project:szksh-lab/2&lt;/span&gt;
      &lt;span class="s"&gt;owner:szksh-lab label:team/sre&lt;/span&gt;
    &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PVT_SRE0000000000000&lt;/span&gt;
  &lt;span class="c1"&gt;# Add issues and pull requests whose repositories are managed by SER team to the project of the SRE team&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;is:open archived:false -project:szksh-lab/2&lt;/span&gt;
      &lt;span class="s"&gt;repo:szksh-lab/k8s-clusters&lt;/span&gt;
      &lt;span class="s"&gt;repo:szksh-lab/aws-org&lt;/span&gt;
    &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PVT_SRE0000000000000&lt;/span&gt;
  &lt;span class="c1"&gt;# Add issues and pull requests which are assigned to the security team to the project of the Security team&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;is:open archived:false -project:szksh-lab/2&lt;/span&gt;
      &lt;span class="s"&gt;owner:szksh-lab is:pr&lt;/span&gt;
      &lt;span class="s"&gt;team-review-requested:szksh-lab/security&lt;/span&gt;
    &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PVT_SECURITY00000000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;In this post, I explained how to automatically gather GitHub Issues and Pull Requests across multiple GitHub repositories into GitHub Projects.&lt;br&gt;
&lt;a href="https://suzuki-shunsuke.github.io/profile/oss-development" rel="noopener noreferrer"&gt;I'm maintaining a lot of OSS projects&lt;/a&gt;, and managing their issues and pull requests has been challenging.&lt;br&gt;
Using this approach, I could manage them all in one GitHub Project, which was incredibly helpful.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/orgs/szksh-lab/projects/1/views/1" rel="noopener noreferrer"&gt;https://github.com/orgs/szksh-lab/projects/1/views/1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this post helps you improve the management of your issues and pull requests.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Set GitHub Actions timeout-minutes</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Wed, 03 Jul 2024 02:46:43 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/set-github-actions-timeout-minutes-1jkk</link>
      <guid>https://dev.to/suzukishunsuke/set-github-actions-timeout-minutes-1jkk</guid>
      <description>&lt;p&gt;In this article I introduce a GitHub Actions' setting &lt;code&gt;timeout-minutes&lt;/code&gt; and tools related to &lt;code&gt;timeout-minutes&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's timeout-minutes?&lt;/li&gt;
&lt;li&gt;Why should you set timeout-minutes?&lt;/li&gt;
&lt;li&gt;Linters to enforce timeout-minutes&lt;/li&gt;
&lt;li&gt;A command line tool to set timeout-minutes to all GitHub Actions jobs&lt;/li&gt;
&lt;li&gt;Set timeout-minutes to your all repositories&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's timeout-minutes?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes"&gt;timeout-minutes&lt;/a&gt; is the maximum number of minutes to let a job run before GitHub automatically cancels it.&lt;br&gt;
The default value is 360.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why should you set timeout-minutes?
&lt;/h2&gt;

&lt;p&gt;The default value of timeout-minutes is 360, but this is too long for most GitHub Actions jobs.&lt;br&gt;
Even if processes are stuck for some reason, jobs keeps running until the timeout.&lt;br&gt;
This wastes resources uselessly.&lt;br&gt;
By setting timeout-minutes properly, you can notice the issue and resolve it by retrying jobs quickly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://exercism.org/docs/building/github/gha-best-practices#h-set-timeouts-for-workflows"&gt;https://exercism.org/docs/building/github/gha-best-practices#h-set-timeouts-for-workflows&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Linters to enforce timeout-minutes
&lt;/h2&gt;

&lt;p&gt;There are linters to enforce timeout-minutes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/suzuki-shunsuke/ghalint"&gt;ghalint&lt;/a&gt; is a GitHub Actions linter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://lintnet.github.io/"&gt;lintnet&lt;/a&gt; is a general purpose linter powered by Jsonnet&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  ghalint
&lt;/h3&gt;

&lt;p&gt;From ghalint &lt;a href="https://github.com/suzuki-shunsuke/ghalint/releases/tag/v0.2.12"&gt;v0.2.12&lt;/a&gt;, ghalint enforces timeout-minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/012.md"&gt;https://github.com/suzuki-shunsuke/ghalint/blob/main/docs/policies/012.md&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  lintnet
&lt;/h3&gt;

&lt;p&gt;ghalint is ported to lintnet as a module.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lintnet-modules/ghalint"&gt;https://github.com/lintnet-modules/ghalint&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you can enforce timeout-minutes using the module.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/lintnet-modules/ghalint/tree/main/workflow/job_timeout_minutes_is_required"&gt;https://github.com/lintnet-modules/ghalint/tree/main/workflow/job_timeout_minutes_is_required&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  A command line tool to set timeout-minutes to all GitHub Actions jobs
&lt;/h2&gt;

&lt;p&gt;It is very bothersome to set timeout-minutes to a lot of jobs by hand.&lt;br&gt;
But you can do it easily using a command line tool &lt;a href="https://github.com/suzuki-shunsuke/ghatm"&gt;ghatm&lt;/a&gt;.&lt;br&gt;
It finds GitHub Actions workflows and adds timeout-minutes to jobs which don't have the setting.&lt;br&gt;
It edits workflow files while keeping YAML comments, indents, empty lines, and so on.&lt;/p&gt;

&lt;p&gt;For details, please see &lt;a href="https://github.com/suzuki-shunsuke/ghatm"&gt;https://github.com/suzuki-shunsuke/ghatm&lt;/a&gt; .&lt;/p&gt;
&lt;h2&gt;
  
  
  Set timeout-minutes to your all repositories
&lt;/h2&gt;

&lt;p&gt;ghatm is useful, but it is very bothersome to run ghatm and create and merge pull requests to a lot of repositories by hand.&lt;br&gt;
But you can do it easily using ghatm and &lt;a href="https://github.com/lindell/multi-gitter"&gt;multi-gitter&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create pull requests by &lt;code&gt;multi-gitter run&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;multi-gitter run ./ghatm-set.sh &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--config&lt;/span&gt; config.yaml &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-O&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$org&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"ci: set timeout-minutes using ghatm"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--skip-forks&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-b&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-B&lt;/span&gt; ci-set-timeout-minutes-by-ghatm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;ghatm-set.sh&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ghatm &lt;span class="nb"&gt;set&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;config.yaml&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;git-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cmd&lt;/span&gt; &lt;span class="c1"&gt;# Use git to sign commits&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Merge pull requests by &lt;code&gt;multi-gitter merge&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;multi-gitter merge &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-O&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$org&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-B&lt;/span&gt; ci-set-timeout-minutes-by-ghatm &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--skip-forks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Prevent malicious Terraform Providers</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Sun, 05 Nov 2023 04:30:00 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/prevent-malicious-terraform-providers-m1</link>
      <guid>https://dev.to/suzukishunsuke/prevent-malicious-terraform-providers-m1</guid>
      <description>&lt;p&gt;In this blog post, I describe how &lt;a href="https://github.com/suzuki-shunsuke/tfprovidercheck"&gt;tfprovidercheck&lt;/a&gt; prevents malicious Terraform Providers from being executed.&lt;/p&gt;

&lt;p&gt;To run Terraform securely, we should prevent malicious Terraform Providers from being executed.&lt;br&gt;
&lt;a href="https://github.com/suzuki-shunsuke/tfprovidercheck"&gt;tfprovidercheck&lt;/a&gt; is a simple command line tool for this.&lt;br&gt;
Using tfprovidercheck, you can define the allow list of Terraform Providers and their versions, and check if disallowed providers aren't used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Only google provider and azurerm provider are allowed
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; .tfprovidercheck.yaml
&lt;span class="go"&gt;providers:
  - name: registry.terraform.io/hashicorp/google
&lt;/span&gt;&lt;span class="gp"&gt;    version: "&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; 4.0.0&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;  - name: registry.terraform.io/hashicorp/azurerm

&lt;/span&gt;&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tfprovidercheck fails because aws provider is disallowed
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;terraform version &lt;span class="nt"&gt;-json&lt;/span&gt; | tfprovidercheck
&lt;span class="go"&gt;FATA[0000] tfprovidercheck failed                        error="this Terraform Provider is disallowed" program=tfprovidercheck provider_name=registry.terraform.io/hashicorp/aws tfprovidercheck_version=0.1.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using tfprovidercheck in Terraform CI, you can improve the security of Terraform CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;tfprovidercheck is a single binary written in &lt;a href="https://go.dev/"&gt;Go&lt;/a&gt;. So you only need to install an execurable file into &lt;code&gt;$PATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Please see &lt;a href="https://github.com/suzuki-shunsuke/tfprovidercheck#install"&gt;Install&lt;/a&gt;.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Prepare tfprovider's &lt;a href="https://github.com/suzuki-shunsuke/tfprovidercheck#configuration"&gt;configuration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform init&lt;/code&gt; to update the list of Terraform Providers&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;terraform version -json | tfprovidercheck&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To prevent malicious codes from being executed, you should run tfprovidercheck before running other Terraform commands such as &lt;code&gt;terraform validate&lt;/code&gt;, &lt;code&gt;terraform plan&lt;/code&gt;, and &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;There are several ways to configure tfprovidercheck.&lt;br&gt;
In order of priority, they are as follows.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The command line option &lt;code&gt;-config [-c]&lt;/code&gt;, which is the configuration file path&lt;/li&gt;
&lt;li&gt;The environment variable &lt;code&gt;TFPROVIDERCHECK_CONFIG_BODY&lt;/code&gt;, which is the configuration itself (YAML)&lt;/li&gt;
&lt;li&gt;The environment variable &lt;code&gt;TFPROVIDERCHECK_CONFIG&lt;/code&gt;, which is the configuration file path&lt;/li&gt;
&lt;li&gt;The configuration file &lt;code&gt;.tfprovidercheck.yaml&lt;/code&gt; on the current directory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The field &lt;code&gt;providers&lt;/code&gt; lists allowed providers and their versions.&lt;/p&gt;

&lt;p&gt;e.g.&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;providers&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;registry.terraform.io/hashicorp/aws&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;3.0.0"&lt;/span&gt; &lt;span class="c1"&gt;# Quotes are necessary because '&amp;gt;' is a special character for YAML&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;registry.terraform.io/hashicorp/google&lt;/span&gt;
    &lt;span class="c1"&gt;# version is optional&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; (Required, string): &lt;code&gt;name&lt;/code&gt; must be equal to the provider name. Regular expression and glob aren't supported&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt; (Optional, string): The version constraint of Terraform Provider. &lt;code&gt;version&lt;/code&gt; is evaluated as &lt;a href="https://github.com/hashicorp/go-version#version-constraints"&gt;hashicorp/go-version' Version Constraints&lt;/a&gt;. If &lt;code&gt;version&lt;/code&gt; is empty, any version is allowed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💡 Prevent configuration from being tampered
&lt;/h2&gt;

&lt;p&gt;It's important to prevent configuration from being tamperd.&lt;br&gt;
If you run tfprovidercheck on GitHub Actions, &lt;code&gt;pull_request_target&lt;/code&gt; event is useful to prevent workflows from being tampered.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/suzukishunsuke/secure-github-actions-by-pullrequesttarget-641"&gt;Secure GitHub Actions by pull_request_target&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;tfprovidercheck supports configuring with the environment variable &lt;code&gt;TFPROVIDERCHECK_CONFIG_BODY&lt;/code&gt;, so you can define the configuraiton in a workflow file.&lt;/p&gt;

&lt;p&gt;e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terraform version -json | tfprovidercheck&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;TFPROVIDERCHECK_CONFIG_BODY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;providers:&lt;/span&gt;
        &lt;span class="s"&gt;- name: registry.terraform.io/hashicorp/aws&lt;/span&gt;
          &lt;span class="s"&gt;version: "&amp;gt;= 3.0.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can prevent configuration from being tampered by &lt;code&gt;pull_request_target&lt;/code&gt; event.&lt;/p&gt;

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

&lt;p&gt;In this blog post, I described how &lt;a href="https://github.com/suzuki-shunsuke/tfprovidercheck"&gt;tfprovidercheck&lt;/a&gt; prevents malicious Terraform Providers from being executed.&lt;/p&gt;

&lt;p&gt;Please try &lt;a href="https://github.com/suzuki-shunsuke/tfprovidercheck"&gt;tfprovidercheck&lt;/a&gt; and give me your feedback!&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>security</category>
      <category>ci</category>
    </item>
    <item>
      <title>Secure GitHub Actions by pull_request_target</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Mon, 23 Oct 2023 01:34:11 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/secure-github-actions-by-pullrequesttarget-641</link>
      <guid>https://dev.to/suzukishunsuke/secure-github-actions-by-pullrequesttarget-641</guid>
      <description>&lt;p&gt;In this post, I describe how to build secure GitHub Actions workflows by &lt;code&gt;pull_request_target&lt;/code&gt; event instead of &lt;code&gt;pull_request&lt;/code&gt; event.&lt;br&gt;
This post is based on my post written in Japanese. &lt;a href="https://zenn.dev/shunsuke_suzuki/articles/secure-github-actions-by-pull-request-target"&gt;pull_request_target で GitHub Actions の改竄を防ぐ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub Actions is one of the most popular CI platform.&lt;br&gt;
GitHub Actions is powerful, but has a security concern that workflow files &lt;code&gt;.github/workflows/*.yaml&lt;/code&gt; can be tampered and malicious codes can be executed with secrets and permissions in CI.&lt;br&gt;
To solve the issue, I propose using GitHub Actions' &lt;a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target"&gt;pull_request_target&lt;/a&gt; event instead of &lt;code&gt;pull_request&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;Note that in this post I talk about the enterprise software development on private repositories rather than OSS activities on public repositories, and I assume pull requests aren't sent from Fork repositories.&lt;/p&gt;
&lt;h2&gt;
  
  
  Before using &lt;code&gt;pull_request_target&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before using &lt;code&gt;pull_request_target&lt;/code&gt;, you should utilize GitHub features such as &lt;a href="https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/managing-a-branch-protection-rule"&gt;Branch protection rules&lt;/a&gt;, &lt;a href="https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners"&gt;code owners&lt;/a&gt;, and &lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect"&gt;OIDC&lt;/a&gt;, and so on for security.&lt;br&gt;
In this post I assume you are utilizing them properly already. Using &lt;code&gt;pull_request_target&lt;/code&gt; is a more advanced topic.&lt;/p&gt;
&lt;h2&gt;
  
  
  What and Why pull_request_target?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target"&gt;pull_request_target&lt;/a&gt; is one of the events triggering GitHub Actions workflows.&lt;br&gt;
One of the differences between pull_request_target and pull_request is that pull_request_target triggers workflows based on the latest commit of the pull request's base branch.&lt;br&gt;
Even if workflow files are modified or deleted on feature branches, workflows on the default branch aren't affected so you can prevent malicious code from being executed in CI without code review.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example of pull_request_target
&lt;/h3&gt;

&lt;p&gt;If you aren't familiar with pull_request_target and you can't understand how it prevents tampering, please add the following workflow to your repository's default branch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request_target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Use pull_request_target&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&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;test&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;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "$EVENT"&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;EVENT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{toJSON(github)}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then please modify the workflow file and create a pull request to the default branch.&lt;br&gt;
The workflow would be run based on the workflow file of the base branch and your modification wouldn't affect to the workflow run.&lt;br&gt;
And even if you remove the workflow from the feature branch, the workflow would be run.&lt;br&gt;
So malicious codes can't be run in CI unless they are merged into the default branch.&lt;/p&gt;

&lt;p&gt;This is one of the diffrences between pull_request and pull_request_target.&lt;/p&gt;
&lt;h2&gt;
  
  
  Don't execute actions and scripts of feature branches
&lt;/h2&gt;

&lt;p&gt;You shouldn't execute actions and scripts of feature branches because they can be tampered.&lt;br&gt;
If you want to execute them, you should get them from safe other repositories or branches such as the default branch.&lt;/p&gt;
&lt;h2&gt;
  
  
  Secure OIDC Settings
&lt;/h2&gt;

&lt;p&gt;To access Cloud Providers such as AWS and Google Cloud, you should use OIDC rather than secrets in terms of security.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect"&gt;https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub supports various OIDC claims.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#understanding-the-oidc-token"&gt;https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#understanding-the-oidc-token&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://token.actions.githubusercontent.com/.well-known/openid-configuration"&gt;https://token.actions.githubusercontent.com/.well-known/openid-configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can prevent malicious authentication to OIDC with the following claims.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repo&lt;/li&gt;
&lt;li&gt;event_name&lt;/li&gt;
&lt;li&gt;base_ref&lt;/li&gt;
&lt;li&gt;ref&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to allow the authentication only on the specific workflows, you can use the claim &lt;code&gt;workflow&lt;/code&gt; too.&lt;/p&gt;

&lt;p&gt;I describe OIDC settings on AWS and Google Cloud.&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services"&gt;https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You create two IAM Roles.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;IAM Role for the default branch can create, read, update, and delete resources&lt;/li&gt;
&lt;li&gt;IAM Role for pull requests can read resources&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can restrict the authentication to those IAM Roles by the following IAM Role's trust policy.&lt;/p&gt;

&lt;p&gt;For the default branch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repo:octo-org/octo-repo:event_name:push:base_ref::ref:refs/heads/main"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For pull_request_target&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"Condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StringEquals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:aud"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"StringLike"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token.actions.githubusercontent.com:sub"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repo:octo-org/octo-repo:event_name:pull_request_target:base_ref:main:*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can prevent the following attacks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assume the IAM Role for the default branch on pull request

&lt;ul&gt;
&lt;li&gt;This is impossible because only push event to the default branch is allowed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Assume the IAM Role for pull requests by running malicious workflows with pull_request event

&lt;ul&gt;
&lt;li&gt;This is impossible because only pull_request_target event is allowed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Assume the IAM Role for pull requests by adding malicious workflows to any feature branches and sending pull requests with pull_request_target event to the branches

&lt;ul&gt;
&lt;li&gt;This is impossible because base_ref must be main&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;In case of AWS, you need to &lt;a href="https://docs.github.com/en/rest/actions/oidc?apiVersion=2022-11-28#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository"&gt;set the customization template for an OIDC subject claim for the GitHub repository&lt;/a&gt;.&lt;br&gt;
Otherwise, the authentication would fail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh api &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--method&lt;/span&gt; PUT &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/vnd.github+json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-GitHub-Api-Version: 2022-11-28"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"/repos/&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;/actions/oidc/customization/sub"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="nv"&gt;use_default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"include_claim_keys[]=repo"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"include_claim_keys[]=event_name"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"include_claim_keys[]=base_ref"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"include_claim_keys[]=ref"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Google Cloud
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform"&gt;https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/iam/docs/workload-identity-federation"&gt;https://cloud.google.com/iam/docs/workload-identity-federation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You create two Service Accounts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Service Account for the default branch can create, read, update, and delete resources&lt;/li&gt;
&lt;li&gt;Service Account for pull requests can read resources&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can restrict the authentication to those Service Accounts by the following Attribute mappings and Attribute conditions.&lt;/p&gt;

&lt;p&gt;Attribute mapping&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attribute.repository = assertion.repository
attribute.event_name = assertion.event_name
attribute.base_ref   = assertion.base_ref
attribute.ref        = assertion.ref
attribute.workflow   = assertion.workflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attribute conditions&lt;/p&gt;

&lt;p&gt;For CI on Pull Request&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attribute.repository == "kouzoh/microservices-terraform" &amp;amp;&amp;amp; 
  attribute.event_name == "pull_request_target" &amp;amp;&amp;amp;
  attribute.base_ref == "master"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For CI on the default branch&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;attribute.repository == "octo-org/octo-repo" &amp;amp;&amp;amp; 
  attribute.event_name == "push" &amp;amp;&amp;amp;
  attribute.ref == "refs/heads/main"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can prevent attacks same with AWS.&lt;br&gt;
Unlike AWS, you don't have to &lt;a href="https://docs.github.com/en/rest/actions/oidc?apiVersion=2022-11-28#set-the-customization-template-for-an-oidc-subject-claim-for-a-repository"&gt;set the customization template for an OIDC subject claim for the repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Secret Management
&lt;/h2&gt;

&lt;p&gt;To access secrets securely in CI, you should manage them in secrets management services such as &lt;a href="https://aws.amazon.com/secrets-manager/"&gt;AWS Secrets Manager&lt;/a&gt; and &lt;a href="https://cloud.google.com/secret-manager"&gt;Google Secret Manager&lt;/a&gt; and access them via OIDC so that you can restrict access to them with OIDC claims.&lt;br&gt;
&lt;a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#environment-secrets"&gt;GitHub's Environment Secrets&lt;/a&gt; can also restrict the access but it supports only the restriction based on branches, so malicious workflows can access secrets for pull request CI.&lt;br&gt;
As I described in the previous section, OIDC supports more flexible restrictions, so they are better than GitHub Secrets in terms of security.&lt;/p&gt;
&lt;h2&gt;
  
  
  Modify workflows for pull_request_target
&lt;/h2&gt;

&lt;p&gt;The GitHub Actions &lt;a href="https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables"&gt;built in environment variables&lt;/a&gt; and &lt;a href="https://docs.github.com/en/actions/learn-github-actions/contexts"&gt;Context&lt;/a&gt; of pull_request_target event are different from those of pull_request event.&lt;br&gt;
For example, the following environment variables and context are different.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;event_name, GITHUB_EVENT_NAME&lt;/li&gt;
&lt;li&gt;ref, GITHUB_REF&lt;/li&gt;
&lt;li&gt;sha, GITHUB_SHA&lt;/li&gt;
&lt;li&gt;ref_name, GITHUB_REF_NAME&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may need to fix scripts and actions so that they work well on pull_request_target events.&lt;br&gt;
For example, if you use &lt;a href="https://github.com/suzuki-shunsuke/tfcmt"&gt;tfcmt&lt;/a&gt; and &lt;a href="https://github.com/suzuki-shunsuke/github-comment"&gt;github-comment&lt;/a&gt;, which are my OSS, you need to set the merge commit hash to the environment variables &lt;code&gt;TFCMT_SHA&lt;/code&gt; and &lt;code&gt;GH_COMMENT_SHA1&lt;/code&gt;.&lt;br&gt;
You also need to check if third party actions support the pull_request_target event.&lt;/p&gt;
&lt;h2&gt;
  
  
  Checkout merge commits
&lt;/h2&gt;

&lt;p&gt;To checkout the merged commit with &lt;a href="https://github.com/actions/checkout"&gt;actions/checkout&lt;/a&gt; on pull_request_target event, you need to &lt;a href="https://docs.github.com/en/free-pro-team@latest/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request"&gt;get the pull request by GitHub API&lt;/a&gt; and set the merge commit hash to &lt;code&gt;actions/checkout&lt;/code&gt; input &lt;code&gt;ref&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/github-script@v6&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;pr&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;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;const { data: pullRequest } = await github.rest.pulls.get({&lt;/span&gt;
        &lt;span class="s"&gt;...context.repo,&lt;/span&gt;
        &lt;span class="s"&gt;pull_number: context.payload.pull_request.number,&lt;/span&gt;
      &lt;span class="s"&gt;});&lt;/span&gt;
      &lt;span class="s"&gt;return pullRequest&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&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;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{fromJSON(steps.pr.outputs.result).merge_commit_sha}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created &lt;a href="https://github.com/suzuki-shunsuke/get-pr-action"&gt;a small action&lt;/a&gt; for this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;suzuki-shunsuke/get-pr-action@v0.1.0&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;pr&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&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;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{steps.get-pr.outputs.merge_commit_sha}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is useless to call the GitHub API to get the merge commit hash everytime you run &lt;code&gt;actions/checkout&lt;/code&gt;, so it's good to get the merge commit hash in one job and pass the merge commit hash by the job's output.&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;get-pr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;outputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;merge_commit_sha&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{steps.prs.outputs.merge_commit_sha}}&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;suzuki-shunsuke/get-pr-action@v0.1.0&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;pr&lt;/span&gt;
  &lt;span class="na"&gt;foo&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;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;get-pr&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&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;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{needs.get-pr.outputs.merge_commit_sha}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the context value &lt;code&gt;${{github.event.pull_request.merge_commit_sha}}&lt;/code&gt; isn't the latest merge commit hash.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test of workflow changes
&lt;/h2&gt;

&lt;p&gt;One of the drawbacks of pull_request_target is that it's difficult to test changes of GitHub Actions workflows in CI because changes aren't reflected until they are merged to the default branch.&lt;br&gt;
Especially, if pull requests by Renovate are merged automatically, workflows may be broken suddenly.&lt;/p&gt;

&lt;p&gt;To solve the issue, maybe you can run workflows with test files when workflow files are modified.&lt;br&gt;
By separating workflows as reusable workflows, maybe you can test workflow changes with test inputs.&lt;/p&gt;

&lt;p&gt;About Renovate, disabling auto-merge of actions updates is also one of options.&lt;/p&gt;

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

&lt;p&gt;In this post, I described how to build secure GitHub Actions workflows by &lt;code&gt;pull_request_target&lt;/code&gt; event instead of &lt;code&gt;pull_request&lt;/code&gt; event.&lt;br&gt;
Using &lt;code&gt;pull_request_target&lt;/code&gt;, you can prevent malicious codes from being executed in CI.&lt;br&gt;
And by managing secrets in secrets management services such as &lt;a href="https://aws.amazon.com/secrets-manager/"&gt;AWS Secrets Manager&lt;/a&gt; and &lt;a href="https://cloud.google.com/secret-manager"&gt;Google Secret Manager&lt;/a&gt; and access them via OIDC, you can restrict the access to secrets securely.&lt;br&gt;
To migrate &lt;code&gt;pull_request&lt;/code&gt; to &lt;code&gt;pull_request_target&lt;/code&gt;, several modifications are needed.&lt;br&gt;
And &lt;code&gt;pull_request_target&lt;/code&gt; has a drawback that it's difficult to test changes of workflows, so it's good to introduce &lt;code&gt;pull_request_target&lt;/code&gt; to repositories that require strong permissions in CI.&lt;br&gt;
For example, a Terraform Monorepo tends to require strong permissions for CI, so it's good to introduce &lt;code&gt;pull_request_target&lt;/code&gt; to it.&lt;/p&gt;

</description>
      <category>security</category>
      <category>githubactions</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
    <item>
      <title>Terraform's Drift Detection by tfaction</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Mon, 05 Jun 2023 00:28:41 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/terraforms-drift-detection-by-tfaction-1dkh</link>
      <guid>https://dev.to/suzukishunsuke/terraforms-drift-detection-by-tfaction-1dkh</guid>
      <description>&lt;p&gt;In this article I describe the overview of Terraform's Drift Detection by tfaction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://suzuki-shunsuke.github.io/tfaction/docs/feature/drift-detection"&gt;https://suzuki-shunsuke.github.io/tfaction/docs/feature/drift-detection&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2BgGdt4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/suzuki-shunsuke/tfaction-docs/assets/13323303/2e95f528-8c5d-410c-8dec-fe0dabd3e85a" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2BgGdt4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/suzuki-shunsuke/tfaction-docs/assets/13323303/2e95f528-8c5d-410c-8dec-fe0dabd3e85a" alt="image" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's tfaction?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://suzuki-shunsuke.github.io/tfaction/docs/"&gt;https://suzuki-shunsuke.github.io/tfaction/docs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;tfaction is a framework for Monorepo to build high level Terraform Workflows by GitHub Actions. You don't have to run terraform apply in your laptop, and don't have to reinvent the wheel for Terraform Workflows anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's drift?
&lt;/h2&gt;

&lt;p&gt;In the context of IaC, &lt;code&gt;drift&lt;/code&gt; means the divergence between the code and infrastructure.&lt;br&gt;
Drift harms not only the reliability of the code but also the productivity.&lt;br&gt;
So you should detect and resolve the drift as soon as possible.&lt;/p&gt;

&lt;p&gt;In case of Terraform, the drift causes the unexpected changes of &lt;code&gt;terraform plan&lt;/code&gt;. Unexpected changes confuse you and let you handle them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's tfaction's Drift Detection?
&lt;/h2&gt;

&lt;p&gt;From tfaction v0.6.0, tfaction supports Drift Detection.&lt;br&gt;
tfaction enables you to detect the drift periodically and manage the drift as GitHub Issues.&lt;/p&gt;

&lt;p&gt;This feature is disabled by default. To enable, please see &lt;a href="https://suzuki-shunsuke.github.io/tfaction/docs/feature/drift-detection#set-up"&gt;the document&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;tfaction creates an Issue per working directory.&lt;br&gt;
tfaction checks if the drift exists at the following timing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apply&lt;/code&gt; workflow

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apply&lt;/code&gt; workflow is run when the pull request is merged&lt;/li&gt;
&lt;li&gt;If the job succeeds, the issue is closed.&lt;/li&gt;
&lt;li&gt;If the job fails, the issue is reopened.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;schedule-detect-drifts&lt;/code&gt; workflow

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;schedule-detect-drifts&lt;/code&gt; is run periodically&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;terraform plan&lt;/code&gt; has no change, the issue is closed&lt;/li&gt;
&lt;li&gt;If the job fails or &lt;code&gt;terraform plan&lt;/code&gt; has change, the issue is reopened.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;tfaction reopens the issue when the drift is detected, and closes the issue when the drift is resolved.&lt;br&gt;
tfaction posts a comment and updates the issue description according to the result of the drift detection.&lt;/p&gt;

&lt;p&gt;Example 1. An Issue is closed because &lt;code&gt;terraform apply&lt;/code&gt; succeeds and the drift is resolved&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j7iZMOnq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/232356803-e1c7298f-362c-4f00-96f0-20f2ac8720f7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j7iZMOnq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/232356803-e1c7298f-362c-4f00-96f0-20f2ac8720f7.png" alt="image" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example 2. An Issue is opened because &lt;code&gt;terraform apply&lt;/code&gt; fails&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OdzZA-qF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/233077124-4db0f8a5-1f82-4abd-b0b4-fb641fcee85e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OdzZA-qF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/233077124-4db0f8a5-1f82-4abd-b0b4-fb641fcee85e.png" alt="image" width="800" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example 3. Drift is checked by &lt;code&gt;schedule-detect-drifts&lt;/code&gt; periodically&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rfrZCg_M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/233079030-67bd01cc-b6bf-425a-bdeb-82447a31904a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rfrZCg_M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/233079030-67bd01cc-b6bf-425a-bdeb-82447a31904a.png" alt="image" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BuvhiKer--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/233079963-68765f2e-1efd-4278-b6c3-145eae9ef9c0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BuvhiKer--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/233079963-68765f2e-1efd-4278-b6c3-145eae9ef9c0.png" alt="image" width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Example 4. The latest comment is reflected to the issue description&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2BgGdt4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/suzuki-shunsuke/tfaction-docs/assets/13323303/2e95f528-8c5d-410c-8dec-fe0dabd3e85a" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2BgGdt4G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://github.com/suzuki-shunsuke/tfaction-docs/assets/13323303/2e95f528-8c5d-410c-8dec-fe0dabd3e85a" alt="image" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Good point
&lt;/h2&gt;

&lt;p&gt;If you already use GitHub Issues for your task management, you can add drift handling into your task management naturally.&lt;br&gt;
You don't have to create issues yourself. You can manage issues in GitHub Projects, adjust the priority, and assign someone to issues.&lt;/p&gt;

&lt;p&gt;Issue's comments become the history, so you can track when the drift is raised and which pull request caused the drift.&lt;br&gt;
And comments tell you not only the existence of the drift but also the content of the drift.&lt;/p&gt;

&lt;p&gt;You can adjust the frequency of the drift detection, and select workfing directories where the dirft detection is enabled.&lt;/p&gt;

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

&lt;p&gt;In this article I described the overview of Terraform's Drift Detection by tfaction.&lt;br&gt;
About the details, please see the document.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://suzuki-shunsuke.github.io/tfaction/docs/feature/drift-detection"&gt;https://suzuki-shunsuke.github.io/tfaction/docs/feature/drift-detection&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
    </item>
    <item>
      <title>Improve OSS issue management with GitHub Issue/Discussion template and actions</title>
      <dc:creator>Shunsuke Suzuki</dc:creator>
      <pubDate>Sun, 07 May 2023 04:03:39 +0000</pubDate>
      <link>https://dev.to/suzukishunsuke/improve-oss-issue-management-with-github-issuediscussion-template-and-actions-4p49</link>
      <guid>https://dev.to/suzukishunsuke/improve-oss-issue-management-with-github-issuediscussion-template-and-actions-4p49</guid>
      <description>&lt;p&gt;In this post, I describe how I improved some OSS's issue management.&lt;/p&gt;

&lt;p&gt;e.g. &lt;a href="https://github.com/suzuki-shunsuke/tfcmt/pull/764"&gt;https://github.com/suzuki-shunsuke/tfcmt/pull/764&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enforce users to use Discussion instead of Issue
&lt;/h2&gt;

&lt;p&gt;I set up GitHub Actions to close issues created by users automatically.&lt;/p&gt;

&lt;p&gt;e.g. &lt;a href="https://github.com/suzuki-shunsuke/tfcmt/blob/4a164485908ee216cd9534900dea82e652c00b38/.github/workflows/close-issue.yaml"&gt;https://github.com/suzuki-shunsuke/tfcmt/blob/4a164485908ee216cd9534900dea82e652c00b38/.github/workflows/close-issue.yaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3fJhoD9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236376582-547f36e9-316c-4ee4-b79d-3dc972550611.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3fJhoD9---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236376582-547f36e9-316c-4ee4-b79d-3dc972550611.png" alt="image" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Users have to use GitHub Discussions instead. Only maintainers can create issues. This keeps issues maintainable and actionable.&lt;/p&gt;

&lt;p&gt;In many projects, the distinction between the use of Issue and Discussion is ambiguous and varies from person to person.&lt;br&gt;
This ambiguousness often raises friction and frustration.&lt;br&gt;
By enforcing users to use Discussion, the ambiguousness would be solved.&lt;/p&gt;

&lt;p&gt;Before creating an issue, users would be able to understand the rule by issue template's warning.&lt;br&gt;
By disabling blank issues, users can't ignore templates.&lt;/p&gt;

&lt;p&gt;e.g. &lt;a href="https://github.com/suzuki-shunsuke/tfcmt/issues/new/choose"&gt;https://github.com/suzuki-shunsuke/tfcmt/issues/new/choose&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9P2iqSst--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236655501-45979c69-de5a-4f8b-8cbb-d389f857b57e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9P2iqSst--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236655501-45979c69-de5a-4f8b-8cbb-d389f857b57e.png" alt="image" width="800" height="243"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the header of issue templates guides this rule to users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wcr00sRI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236655545-e159ff22-7e62-48bd-b718-9c6484ff1b48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wcr00sRI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236655545-e159ff22-7e62-48bd-b718-9c6484ff1b48.png" alt="image" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion template
&lt;/h2&gt;

&lt;p&gt;I provide various templates for various use cases.&lt;/p&gt;

&lt;p&gt;e.g. &lt;a href="https://github.com/suzuki-shunsuke/tfcmt/discussions/new/choose"&gt;https://github.com/suzuki-shunsuke/tfcmt/discussions/new/choose&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yWnbzA7t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236655869-8a8d2306-8d25-4f20-97e4-c71471cf9ca4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yWnbzA7t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236655869-8a8d2306-8d25-4f20-97e4-c71471cf9ca4.png" alt="image" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the type of categories isn't enough, users would not be able to choose appropriate category and use general purpose category which isn't well-formatted.&lt;/p&gt;

&lt;p&gt;And GitHub's form schema enforces users to write required information and the schema unifies the format of discussions.&lt;br&gt;
This would improve the quality of discussions.&lt;br&gt;
This reduces the miscommunication, the communication to ask required information, and the burden of maintainers.&lt;/p&gt;

&lt;p&gt;e.g. &lt;a href="https://github.com/suzuki-shunsuke/tfcmt/discussions/new?category=bug-report"&gt;https://github.com/suzuki-shunsuke/tfcmt/discussions/new?category=bug-report&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_wCR5NW4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236656006-37b89e9e-d27b-4854-85f9-d68aaf24498b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_wCR5NW4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236656006-37b89e9e-d27b-4854-85f9-d68aaf24498b.png" alt="image" width="800" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The non structured category is also provided for general purpose, but the template header encourage users to use other categories.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--netQ2nZV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236656163-b9d58fd9-b104-4c2f-8717-21751ffeae43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--netQ2nZV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://user-images.githubusercontent.com/13323303/236656163-b9d58fd9-b104-4c2f-8717-21751ffeae43.png" alt="image" width="800" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All categories are &lt;code&gt;Answers enabled&lt;/code&gt;, so we can make the conclusion easy to understand and clarify the status without closing the discussion.&lt;/p&gt;

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