<?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: kosei</title>
    <description>The latest articles on DEV Community by kosei (@koseimori).</description>
    <link>https://dev.to/koseimori</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%2F1328391%2F2a366f22-d317-4f1c-8552-24cfde1d602c.jpeg</url>
      <title>DEV Community: kosei</title>
      <link>https://dev.to/koseimori</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/koseimori"/>
    <language>en</language>
    <item>
      <title>A Practical Guide to Continuous Delivery with GitHub Actions and AWS CDK</title>
      <dc:creator>kosei</dc:creator>
      <pubDate>Sun, 23 Feb 2025 15:41:23 +0000</pubDate>
      <link>https://dev.to/koseimori/a-practical-guide-to-continuous-delivery-with-github-actions-and-aws-cdk-435k</link>
      <guid>https://dev.to/koseimori/a-practical-guide-to-continuous-delivery-with-github-actions-and-aws-cdk-435k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Previously, I wrote an article titled “&lt;a href="https://dev.to/koseimori/implementing-continuous-delivery-for-github-monorepos-and-microservices-with-github-actions-50i8"&gt;Implementing Continuous Delivery for Monorepo and Microservice with GitHub Actions&lt;/a&gt;.” In this article, I would like to dive deeper into a pattern that utilizes AWS as the infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment
&lt;/h2&gt;

&lt;p&gt;In this article, the following tools will be used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;AWS Account&lt;/li&gt;
&lt;li&gt;AWS CDK&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Infrastructure Definition (IaC)
&lt;/h2&gt;

&lt;p&gt;To perform Continuous Delivery (CD) on your infrastructure, you need to define your infrastructure as code (IaC). Although there are various tools available for IaC, here we will use AWS CDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS CDK
&lt;/h3&gt;

&lt;p&gt;Since in my previous article the repository was set up assuming TypeScript, we will also configure AWS CDK with TypeScript. However, as this article focuses on CD, please refer to another article for detailed explanations on AWS CDK.&lt;/p&gt;

&lt;p&gt;We aim for the following directory structure, so run the following command in the services/infrastructure directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./
├── .github/
│   └── workflows/
├── package.json
└── serivces/
    ├── infrastructure/
    ├── service-a/
    └── service-b/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute the following command to initialize AWS CDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx aws-cdk init app &lt;span class="nt"&gt;--language&lt;/span&gt; typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate 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;my-cdk-app/
├── bin/
│   └── infrastructure.ts
├── lib/
│   └── infrastructure-stack.ts
├── node_modules/
├── package.json
├── tsconfig.json
└── cdk.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a sample, the file infrastructure-stack.ts defines a stack that creates an S3 bucket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-s3&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyCdkAppStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create an S3 Bucket&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;MyBucket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-unique-bucket-name-12345&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// ※ Change this to a globally unique name&lt;/span&gt;
      &lt;span class="na"&gt;removalPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DESTROY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Delete the bucket when the stack is deleted&lt;/span&gt;
      &lt;span class="na"&gt;autoDeleteObjects&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Delete objects in the bucket if removalPolicy is DESTROY&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bootstrapping the Environment
&lt;/h3&gt;

&lt;p&gt;Before the initial deployment, you must bootstrap the target AWS account and region so that AWS CDK can deploy your stack. If you need to deploy to multiple regions, perform the bootstrap for each region. Since this is a one-time process per AWS account and region, it does not need to be managed via CD (GitHub) and can be executed from your local development machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cdk bootstrap --region ap-northeast-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Obtaining AWS Credentials
&lt;/h2&gt;

&lt;p&gt;In order to deploy resources to AWS from GitHub Actions, you need to obtain credentials from AWS. Although you could use AWS_ACCESS_KEY and AWS_SECRET_KEY (i.e., access keys), there are several disadvantages to that approach, which is why the OIDC method described later is recommended:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Management cost of secrets&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access keys require regular key rotation, leading to additional operational overhead.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Management cost for IAM users&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since access keys are tied to IAM users, you would need to create IAM users for actions and periodically audit those accounts.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Security risks during operations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access keys are intended for long-term use, so if they are compromised, they can be misused for an extended period.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Inability for fine-grained control on AWS side&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS cannot restrict access based on the repository or branch when using access keys, and it is difficult to change permissions on a per-workflow basis.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Advantages of Using IAM Roles with OpenID Connect
&lt;/h2&gt;

&lt;p&gt;When using AWS from GitHub Actions, obtaining a role session via OpenID Connect (OIDC) offers the following advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;No need to manage long-term credentials&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With OIDC, temporary credentials are issued for each session. This eliminates the need to manage access keys on GitHub, reducing the risk of key leakage and the burden of key rotation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Increased security and minimal permissions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Temporary sessions obtained via OIDC have short validity. Once the session expires, no further actions can be taken, significantly lowering the security risk. By creating an AWS IAM role with only the necessary permissions and obtaining a session for that role, you can minimize the risk of permission leakage.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Seamless integration between GitHub Actions and AWS&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can restrict usage on a per-repository or per-branch basis—ensuring that only requests from specific repositories are valid, or even narrowing it down by branch or tag. Additionally, roles can be finely controlled per workflow (e.g., separate roles for deployment and testing), ensuring only the minimal required permissions are granted.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  OIDC Configuration (AWS)
&lt;/h2&gt;

&lt;p&gt;To accept OIDC tokens from GitHub Actions, you must create an OpenID Connect Provider on the AWS side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an IAM OIDC Provider with CDK
&lt;/h2&gt;

&lt;p&gt;In CDK, you can use the OpenIdConnectProvider from the @aws-cdk/aws-iam module. Open your CDK stack file (e.g. lib/github-oidc-stack.ts) and modify it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;constructs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aws-cdk-lib/aws-iam&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CdkGithubOidcStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the GitHub OIDC Provider&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gitHubOidcProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;OpenIdConnectProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GitHubOidcProvider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://token.actions.githubusercontent.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;clientIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sts.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;thumbprints&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;6938fd4d98bab03faadb97b34396831e3780aea1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parameters are set as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;url: Specify the GitHub Actions OIDC token endpoint:

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://token.actions.githubusercontent.com" rel="noopener noreferrer"&gt;https://token.actions.githubusercontent.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;clientIds: Specify sts.amazonaws.com.&lt;/li&gt;

&lt;li&gt;thumbprints: Specify the SHA-1 fingerprint of GitHub’s root certificate. 

&lt;ul&gt;
&lt;li&gt;Although it changed once in the past, it is currently 6938fd4d98bab03faadb97b34396831e3780aea1.
This creates the OIDC provider required for federated authentication between GitHub and AWS.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Creating an IAM Role for Use from GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Creating the OIDC provider alone does not grant access to AWS resources. You also need to create an IAM role that GitHub Actions jobs can assume. In the role’s trust policy (assume role policy), you must specify that the entity possessing the GitHub Actions OIDC token is trusted.&lt;/p&gt;

&lt;p&gt;Edit your CDK stack file (e.g. lib/github-oidc-stack.ts) as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;githubRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GitHubActionsRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;roleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github-actions-deploy-role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;assumedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WebIdentityPrincipal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;gitHubOidcProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;openIdConnectProviderArn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;StringEquals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token.actions.githubusercontent.com:aud&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sts.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;StringLike&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token.actions.githubusercontent.com:sub&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repo:your-org/your-repo:/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;

  &lt;span class="c1"&gt;// Actions allowed for this role (e.g., S3 management permissions)&lt;/span&gt;
  &lt;span class="na"&gt;managedPolicies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AmazonS3FullAccess&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ManagedPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromAwsManagedPolicyName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AWSCloudFormationFullAccess&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the CDK stack and note the role ARN (e.g., arn:aws:iam::123456789012:role/github-actions-deploy-role).&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring GitHub Actions
&lt;/h3&gt;

&lt;p&gt;After configuring AWS, you need to set up GitHub Actions to assume the IAM role. GitHub Actions provides an official AWS action, aws-actions/configure-aws-credentials, which supports OIDC.&lt;/p&gt;

&lt;p&gt;You can choose the trigger conditions for the workflow; in this example, it will be triggered manually. For more details, see the GitHub documentation on manually running a workflow.&lt;/p&gt;

&lt;p&gt;Finally, don’t forget to add the permissions section.&lt;/p&gt;

&lt;p&gt;Your repository’s .github/workflows/deploy.yml file should be configured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to AWS&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;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;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@v4&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;Configure AWS Credentials&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;aws-actions/configure-aws-credentials@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="c1"&gt;# Specify the role ARN created earlier in CDK&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:iam::your-aws-account-id:role/github-actions-push-image-role&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ap-northeast-1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run deploy command&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run cdk deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;role-to-assume: Specify the ARN of the IAM role you created.&lt;/li&gt;
&lt;li&gt;aws-region: Specify the AWS region to use (e.g., ap-northeast-1).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This configuration automatically obtains an OIDC token, allowing GitHub Actions to use AWS CLI with a temporary STS session. Since there is no need to store access keys (AccessKey/SecretKey) in the repository secrets, the process is both more secure and reduces the key management burden.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Environment Switching and Approval Steps Using GitHub Environments
&lt;/h2&gt;

&lt;p&gt;By using GitHub Environments, you can define settings and manage secrets for each environment (such as staging or production) and even implement an approval process.&lt;/p&gt;

&lt;p&gt;For more details, see Managing Environments for Deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up Environments
&lt;/h3&gt;

&lt;p&gt;Within your GitHub repository, define environments such as “staging” and “production,” and configure secrets (e.g., AWS account, region, parameters) for each environment.&lt;/p&gt;

&lt;p&gt;By setting up Required Reviewers, when a deployment workflow is executed, the workflow will pause until the specified reviewers approve it.&lt;/p&gt;

&lt;p&gt;For example, if you define an environment called production, add the following to your workflow:&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;deploy&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;environment&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;production&lt;/span&gt; &lt;span class="c1"&gt;# Specify the environment name to use&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Switching Environments
&lt;/h3&gt;

&lt;p&gt;Since you will likely use multiple environments, you may want to switch between environments like “staging” and “production” within the same workflow.&lt;/p&gt;

&lt;p&gt;A workflow can accept an environment as an input, so you can switch environments (and thus the deployment target) by configuring it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&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;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS Environment&lt;/span&gt;
    &lt;span class="na"&gt;required&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;choice&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;dev&lt;/span&gt;
      &lt;span class="s"&gt;staging&lt;/span&gt;
      &lt;span class="s"&gt;production&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;deploy&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;environment&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;${{ inputs.env }}&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to dynamically switch settings such as the AWS account ID defined in GitHub environments, enabling you to manage multiple environments within a single workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Information
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Extending the Session Duration
&lt;/h3&gt;

&lt;p&gt;The AWS session obtained using the above configuration is valid for up to 1 hour. If your deployment workflow takes longer and the session expires, you can extend the session duration by adding additional settings to the role. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;githubRole&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;iam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GitHubActionsRole&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;roleName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;github-actions-deploy-role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;maxSessionDuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Valid for 2 hours&lt;/span&gt;
  &lt;span class="c1"&gt;// ... other configurations&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aws</category>
      <category>devops</category>
      <category>cicd</category>
      <category>github</category>
    </item>
    <item>
      <title>Implementing Continuous Delivery for Monorepo and Microservice with GitHub Actions</title>
      <dc:creator>kosei</dc:creator>
      <pubDate>Sun, 17 Mar 2024 11:03:21 +0000</pubDate>
      <link>https://dev.to/koseimori/implementing-continuous-delivery-for-github-monorepos-and-microservices-with-github-actions-50i8</link>
      <guid>https://dev.to/koseimori/implementing-continuous-delivery-for-github-monorepos-and-microservices-with-github-actions-50i8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article, we explore how to implement CD (Continuous Delivery) using GitHub Actions in a monorepo + microservices setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This section outlines the language and directory structure used as prerequisites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Language
&lt;/h3&gt;

&lt;p&gt;The services within the repository are written in TypeScript and managed as workspaces with Yarn Workspaces. They are also assumed to be containerized. However, it is believed that the same mechanism could work even if services are written in different languages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directory Structure
&lt;/h3&gt;

&lt;p&gt;The assumed directory structure is as follows: There is a services directory at the root, and each service is placed in its own directory within this.&lt;/p&gt;

&lt;p&gt;Infrastructure management tools like AWS are also managed within the same repository, under &lt;code&gt;services/infrastructure.&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./
├── .github/
│   └── workflows/
├── package.json
└── serivces/
    ├── infrastructure/
    ├── service-a/
    └── service-b/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;When adopting such a configuration, the scope of deployment becomes a problem. Workflows used in the repository are managed as common resources in &lt;code&gt;.github/workflows&lt;/code&gt;, and if you write the deployment workflows for &lt;code&gt;service-a&lt;/code&gt; and &lt;code&gt;service-b&lt;/code&gt; there, deploying only &lt;code&gt;service-a&lt;/code&gt; will inadvertently cause &lt;code&gt;service-b&lt;/code&gt; to be deployed as well. This can lead to increased workflow execution times and, depending on the configuration, unnecessary deployment actions.&lt;/p&gt;

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

&lt;p&gt;Therefore, let's consider a method to deploy only the services that have been changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 1 (Using &lt;code&gt;paths&lt;/code&gt; Filters)
&lt;/h3&gt;

&lt;p&gt;A possible solution involves utilizing the functionality of including and excluding paths.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore" rel="noopener noreferrer"&gt;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onpushpull_requestpull_request_targetpathspaths-ignore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can define the relevant paths as follows and prepare a workflow for each service accordingly.&lt;/p&gt;

&lt;p&gt;For example, to deploy only &lt;code&gt;service-a&lt;/code&gt;, the configuration might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy service-a&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;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;serivces/service-a/**'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Steps to deploy service-a go here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The disadvantages of this approach include the need to create a separate workflow file for each service, even when the deployment method is the same, which can make workflow file management cumbersome. Additionally, this method does not support dependencies between services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 2 (Using &lt;code&gt;git diff&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Another method is to use Git's capabilities to deploy only the services that have been changed.&lt;/p&gt;

&lt;p&gt;You can detect changes to the targeted services by using a command to get the differences from the last commit, and then deploy only the changed services.&lt;/p&gt;

&lt;p&gt;Let's explore this approach further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The workflow will be documented for manual execution (&lt;code&gt;workflow_dispatch&lt;/code&gt;), but it can also be set up to trigger automatically on &lt;code&gt;push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If there are changes to the overall infrastructure managed by IaC, you'll want to deploy those changes before deploying any services, considering the deployment dependencies. This setup should also accommodate dependencies between services, allowing them to be expressed in the same system.&lt;/p&gt;

&lt;p&gt;Within the overall deployment workflow, implement the following jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change detection job (&lt;code&gt;prepare&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Infrastructure deployment job&lt;/li&gt;
&lt;li&gt;Individual service deployment jobs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This architecture allows for a more flexible deployment process, accommodating both the infrastructure and individual services, and addressing the need to manage dependencies between services.&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%2Feaxx0yna0dkgtw488a29.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%2Feaxx0yna0dkgtw488a29.png" alt="Flowchart of deployment steps" width="528" height="586"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD;
    prepare--&amp;gt;infrastructure;
    infrastructure--&amp;gt;service-a;
    infrastructure--&amp;gt;service-b;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Indeed, having the ability to deploy services individually, in addition to deploying the entire system through one workflow, would offer greater flexibility and convenience. Thus, the solution should also enable workflows to deploy each service separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Detailed Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Change Detection Job (&lt;code&gt;prepare&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;The change detection can be conducted as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use a script within the &lt;code&gt;prepare&lt;/code&gt; job to run a &lt;code&gt;git diff&lt;/code&gt; command against the last commit to identify changed paths.&lt;/li&gt;
&lt;li&gt;Parse the output of the &lt;code&gt;git diff&lt;/code&gt; command to determine which services have been modified. This could involve checking if the changes are within the directories specific to each service.&lt;/li&gt;
&lt;li&gt;Set the output of this job to indicate which services need to be deployed. This can be achieved by setting environment variables or producing a file that subsequent jobs can read to determine whether they should proceed with deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By implementing this job at the beginning of the workflow, you can dynamically decide which subsequent jobs (e.g., infrastructure deployment or individual service deployments) need to run based on the changes detected. This approach minimizes unnecessary deployments, saves time, and ensures that resources are used efficiently.&lt;/p&gt;

&lt;p&gt;For individual service deployment workflows, you can adopt a similar change detection mechanism but scoped to the specific service's directory. This way, each service can have its workflow triggered either manually or automatically upon changes to its relevant files, offering a seamless and flexible deployment process tailored to the needs of each service within the monorepo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  prepare:
    outputs:
      infrastructure-diff-count: ${{ steps.infrastructure_changes.outputs.diff-count }}
      service-a-diff-count: ${{ steps.service_a_changes.outputs.diff-count }}
      service-b-diff-count: ${{ steps.service_b_changes.outputs.diff-count }}

    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 2
      - id: infrastructure_changes
        run: echo diff-count=`git diff HEAD~ --name-only --relative=services/infrastructure | wc -l` &amp;gt;&amp;gt; $GITHUB_OUTPUT
      - id: service_a_changes
        run: echo diff-count=`git diff HEAD~ --name-only --relative=services/service-a | wc -l` &amp;gt;&amp;gt; $GITHUB_OUTPUT
      - id: service_b_changes
        run: echo diff-count=`git diff HEAD~ --name-only --relative=services/service-b | wc -l` &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the key point:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Point&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Set fetch-depth to 2 in actions/checkout@v3&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To obtain the differences from the previous commit using &lt;code&gt;git diff HEAD~&lt;/code&gt;, you need to set the &lt;code&gt;fetch-depth&lt;/code&gt; to 2. If this is not set, the previous commit cannot be fetched, and you will not be able to perform a diff comparison.&lt;br&gt;
This precaution is essential because the default behavior of &lt;code&gt;actions/checkout@v3&lt;/code&gt; is to fetch only the latest commit (i.e., with a &lt;code&gt;fetch-depth&lt;/code&gt; of 1). This shallow fetch is faster as it downloads less history, but it's not suitable for workflows that need to compare changes between commits. Setting &lt;code&gt;fetch-depth&lt;/code&gt; to 2 ensures that the action fetches the last two commits, allowing &lt;code&gt;git diff&lt;/code&gt; to correctly identify any changes made since the last commit. This setup is crucial for the change detection mechanism in CI/CD workflows that rely on comparing different versions of the codebase to determine the scope of changes and the necessity for deployments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Infrastructure Deployment Job
&lt;/h3&gt;

&lt;p&gt;We won't detail the infrastructure deployment code, but we will mainly mention the parts related to the previous job, the change detection job.&lt;/p&gt;

&lt;p&gt;We define a deployment workflow for the infrastructure itself. By defining &lt;code&gt;workflow_dispatch&lt;/code&gt;, we can execute the workflow for the infrastructure itself. Also, to make it callable from the overall workflow, we define &lt;code&gt;workflow_call&lt;/code&gt; as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on:
  workflow_dispatch:
    inputs:
      stage:
        required: true
        type: string
  workflow_call:
    inputs:
      stage:
        required: true
        type: string

jobs:
  deploy:
    # setup, install, deploy, etc.
    # If using AWS CDK, deploy with commands like yarn workspace infrastructure cdk deploy --all --require-approval=never
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the workflow to deploy the infrastructure itself to the overall deployment workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs: # Written to align the indent, but it's the same job as prepare
  infrastructure_deploy:
    if: ${{ !cancelled() &amp;amp;&amp;amp; !failure() &amp;amp;&amp;amp; needs.prepare.outputs.infrastructure-diff-count &amp;gt; 0 }}
    needs:
      - prepare
    uses: '.github/workflows/deploy-infrastructure.yaml'
    with:
      stage: ${{ inputs.stage }}
    secrets: inherit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are the key points:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Point 1&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Use if statements to determine the necessity of job execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;needs.prepare.outputs.infrastructure-diff-count &amp;gt; 0&lt;/code&gt; to get the number of changed files obtained from the previous job and execute only if there are changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Point 2&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Use needs to indicate dependencies&lt;/strong&gt;&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;needs: 
  - prepare
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;shows that it depends on the prepare job. It is possible to specify multiple dependencies, which can represent waiting until the deployment of the infrastructure or another dependent service is completed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Point 3&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Call the individual infrastructure workflow with uses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can call another workflow set up with &lt;code&gt;workflow_call&lt;/code&gt; using &lt;code&gt;uses: path to workflow file&lt;/code&gt;. If secrets are needed, they can be passed when calling. Specifying &lt;code&gt;inherit&lt;/code&gt; allows passing them collectively, but it's also possible to specify them individually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Deployment Jobs for Each Service
&lt;/h3&gt;

&lt;p&gt;The deployment jobs for each service are almost the same as those for deploying infrastructure, so only the key points are described.&lt;/p&gt;

&lt;p&gt;We define a deployment workflow for each individual service. Services that can be deployed with the same workflow will reuse that workflow by accepting the service name as input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;on:
  workflow_dispatch:
    inputs:
      stage:
        required: true
        type: string
      service:
       required: true
       type: choice
       options:
         - service-a
         - service-b
  workflow_call:
    inputs:
      stage:
        required: true
        type: string
      service:
       required: true
       type: string

jobs:
  deploy:
    # setup, install, deploy, etc.
    # The workflow is reused by switching the target service based on inputs.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the workflow for deploying individual infrastructure to the overall deployment workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs: # Written to align the indentation, but it's the same job as prepare
  service_a_deploy:
    if: ${{ !cancelled() &amp;amp;&amp;amp; !failure() &amp;amp;&amp;amp; needs.prepare.outputs.service-a-diff-count &amp;gt; 0 }}
    needs:
      - prepare
      - infrastructure_deploy
    uses: '.github/workflows/deploy-service.yaml'
    with:
      stage: ${{ inputs.stage }}
      service: service-a
    secrets: inherit

  service_b_deploy:
    if: ${{ !cancelled() &amp;amp;&amp;amp; !failure() &amp;amp;&amp;amp; needs.prepare.outputs.service-b-diff-count &amp;gt; 0 }}
    needs:
      - prepare
      - infrastructure_deploy
      # - service_a_deploy
      # If dependent on service-a, specify as above to wait for the completion of service-a's deployment
    uses: '.github/workflows/deploy-service.yaml'
    with:
      stage: ${{ inputs.stage }}
      service: service-b
    secrets: inherit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key point is as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Point&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Add cancelled(), failure() to the if statement to determine whether to execute the job&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;!cancelled() &amp;amp;&amp;amp; !failure() &amp;amp;&amp;amp;&lt;/code&gt; to the conditions of the if statement. This is to ensure that the workflow stops if it is cancelled, and also to ensure that this job does not skip if the jobs it depends on are skipped.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;With GitHub monorepo + microservices, it was possible to deploy only the services that have changes. The triggers for the workflow and the conditions for detecting changes can be adjusted according to each project.&lt;/p&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
      <category>cicd</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Conditional Branching with workflow_dispatch and workflow_call in GitHub Actions</title>
      <dc:creator>kosei</dc:creator>
      <pubDate>Fri, 08 Mar 2024 17:41:05 +0000</pubDate>
      <link>https://dev.to/koseimori/conditional-branching-with-workflowdispatch-and-workflowcall-in-github-actions-oik</link>
      <guid>https://dev.to/koseimori/conditional-branching-with-workflowdispatch-and-workflowcall-in-github-actions-oik</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;There's a feature in GitHub Actions for reusing workflows. With workflow reuse, you can call another workflow from a workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/using-workflows/reusing-workflows"&gt;https://docs.github.com/en/actions/using-workflows/reusing-workflows&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In doing so, there was a need to branch depending on whether the called workflow was called from another workflow, or if the workflow was executed standalone. This article describes how to do that.&lt;/p&gt;

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

&lt;p&gt;In GitHub Actions, you can obtain various information from the context at runtime.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/actions/learn-github-actions/contexts"&gt;https://docs.github.com/en/actions/learn-github-actions/contexts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Among these, there exists a key named &lt;code&gt;github.event.workflow&lt;/code&gt;. This value contains the file path of the workflow.&lt;/p&gt;

&lt;p&gt;Depending on how it was called, the called workflow can obtain the following values (in the case where workflowA calls workflowB):&lt;/p&gt;

&lt;p&gt;When executing workflowB directly (&lt;code&gt;workflow_dispatch&lt;/code&gt;), the value of &lt;code&gt;github.event.workflow&lt;/code&gt;&lt;br&gt;
The file path of workflowB (&lt;code&gt;.github/workflows/workflow-b.yaml&lt;/code&gt;)&lt;br&gt;
When executed from workflowA (&lt;code&gt;workflow_call&lt;/code&gt;), the value of &lt;code&gt;github.event.workflow&lt;/code&gt;&lt;br&gt;
The file path of workflowA (&lt;code&gt;.github/workflows/workflow-a.yaml&lt;/code&gt;)&lt;br&gt;
With this, you can branch your process or steps.&lt;/p&gt;

&lt;p&gt;For example, if you want to execute a step only when workflowB is executed directly, you can add the following condition:&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;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event.workflow == '.github/workflows/workflow-b.yaml'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also a &lt;code&gt;github.event_name&lt;/code&gt;, and I tried to see if it could be used for branching as well, but both the direct execution of workflowB and the execution from workflowA resulted in the same value as &lt;code&gt;workflow_dispatch&lt;/code&gt;, so it couldn't be used for branching.&lt;/p&gt;

&lt;h2&gt;
  
  
  About type: environment
&lt;/h2&gt;

&lt;p&gt;In practice, I think you can use the same workflow without branching by defining inputs with the same name as below, but since &lt;code&gt;type: environment&lt;/code&gt; exists only for &lt;code&gt;workflow_dispatch&lt;/code&gt;, if you are using this, you'll need to add a step to absorb the difference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;input1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;    
  &lt;span class="na"&gt;workflow_call&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;input1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;required&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;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value in the environment (environment_name) can be used as is, but as shown above, this difference occurs, so it seems better to avoid it when making a &lt;code&gt;workflow_call&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;There seem to be various methods, but by checking &lt;code&gt;github.event.workflow&lt;/code&gt;, we were able to branch based on the method of executing the workflow.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
    <item>
      <title>How to Set Dynamic Release URLs in Environment with GitHub Actions</title>
      <dc:creator>kosei</dc:creator>
      <pubDate>Thu, 07 Mar 2024 16:50:10 +0000</pubDate>
      <link>https://dev.to/koseimori/how-to-set-dynamic-release-urls-in-environment-with-github-actions-l83</link>
      <guid>https://dev.to/koseimori/how-to-set-dynamic-release-urls-in-environment-with-github-actions-l83</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This article explains how to dynamically set a release URL in an Environment using GitHub Actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;GitHub Actions has a feature for defining environments called Environment.&lt;br&gt;
&lt;a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment" rel="noopener noreferrer"&gt;Using environments for deployment - GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, by setting up environments like the following, it's possible to switch the release destination. (You can set any name you want.)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production environment&lt;/li&gt;
&lt;li&gt;Staging environment&lt;/li&gt;
&lt;li&gt;Testing environment&lt;/li&gt;
&lt;li&gt;Development environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Among these, there is a function to display the URL of the release destination after releasing.&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96fcdpl6ciekzdfqn609.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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F96fcdpl6ciekzdfqn609.png" alt="GitHub Actions Release URL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If there's only one fixed environment, it's not a problem. However, if the URL is determined dynamically during the release steps, it was unclear how to display it, so I'm documenting the method here.&lt;/p&gt;

&lt;p&gt;If it's a single environment, you can set the URL as follows.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&lt;a href="https://example.com" rel="noopener noreferrer"&gt;https://example.com&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  How To&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;The method is simple: save the value in an output during the step when the URL is confirmed, and then set that output as the environment's URL.&lt;/p&gt;

&lt;p&gt;Here is a sample Yaml. You need to assign a reference ID to the step. The format for referencing the output is &lt;code&gt;steps.[step_id].outputs.[output_name]&lt;/code&gt;.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;development&lt;/span&gt;&lt;br&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.get_release_url.outputs.release_url }}&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set release url&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;get_release_url&lt;/span&gt;&lt;br&gt;
      &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo release_url=&lt;a href="https://xxx.example.com" rel="noopener noreferrer"&gt;https://xxx.example.com&lt;/a&gt; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Conclusion&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;The method to dynamically set the URL of a release in GitHub Actions' Environment is to save a value in the output during the release step, and then set that output name as the URL in the environment.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Removing "✨ Done in X.XXs." with Yarn</title>
      <dc:creator>kosei</dc:creator>
      <pubDate>Wed, 06 Mar 2024 16:33:56 +0000</pubDate>
      <link>https://dev.to/koseimori/removing-done-in-xxxs-with-yarn-2map</link>
      <guid>https://dev.to/koseimori/removing-done-in-xxxs-with-yarn-2map</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I wanted to remove the "✨ Done in X.XXs." message in Yarn. In the package.json, I defined the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "scripts": {
    "echo": "echo aaa"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When executed, it displays the "✨ Done in X.XXs." message.&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="nv"&gt;$ &lt;/span&gt;yarn run &lt;span class="nb"&gt;echo
&lt;/span&gt;yarn run v1.22.10
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;aaa
aaa
✨  Done &lt;span class="k"&gt;in &lt;/span&gt;0.04s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  --silent Option
&lt;/h2&gt;

&lt;p&gt;By adding the --silent option, it disappeared.&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="nv"&gt;$ &lt;/span&gt;yarn run &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nb"&gt;echo
&lt;/span&gt;aaa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>yarn</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>How to Reset Your User Password in Ubuntu on WSL if You Forget It</title>
      <dc:creator>kosei</dc:creator>
      <pubDate>Tue, 05 Mar 2024 15:31:09 +0000</pubDate>
      <link>https://dev.to/koseimori/how-to-reset-your-user-password-in-ubuntu-on-wsl-if-you-forget-it-48pe</link>
      <guid>https://dev.to/koseimori/how-to-reset-your-user-password-in-ubuntu-on-wsl-if-you-forget-it-48pe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;After a long time, I tried to launch WSL and sudo on Ubuntu, but I forgot the password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reset Method
&lt;/h2&gt;

&lt;p&gt;Log in as root with the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enter the username you want to reset in place of &lt;code&gt;[username]&lt;/code&gt; in the following command.&lt;br&gt;
You will be prompted for a new password, so reset it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;username&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;



</description>
      <category>wsl</category>
      <category>windows</category>
      <category>beginners</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
