Gloweet
    • Projects
    • Contact me
    • About me
    • Bio
    • Expertises
    • Stay updated
    • Blogs
    • Newsletter
Language
Language
New project
Antonin Marxer's Blog
CI/CD deep-dive: Deploy a scalable Multi-Environment React App to AWS S3 + CloudFront with GitHub Actions
CI/CD deep-dive: Deploy a scalable Multi-Environment React App to AWS S3 + CloudFront with GitHub Actions

CI/CD deep-dive: Deploy a scalable Multi-Environment React App to AWS S3 + CloudFront with GitHub Actions

Learn how to build and deploy a scalable multi-environment React + Vite app using Domain-Driven Design, GitHub Actions for CI/CD, and AWS S3 + CloudFront for fast, cost-effective static hosting. Includes environment-specific configs, branch-based workflows, and secure AWS deployment setup.

Antonin Marxer

·

Last updated on August 30, 2025

·

10min read

Table of contents

  • Maintaining code standards
  • Hosting: AWS S3 + CloudFront vs Netlify/Render/Cloudflare
  • S3 + CloudFront (Static Hosting + CDN + SSL)
  • Alternatives (Cloudflare Pages / Netlify / Render)
  • Multi-environment deployment
  • CI — GitHub Actions: Automated & Manual Workflows
  • Auto-Deploy by Branch Name
  • Manual Deploys with workflow\dispatch
  • CD — Deploy to AWS (S3 + CloudFront)
  • 1. Set up AWS IAM OIDC Provider
  • 2. Create an IAM Role for the Github Actions to assume
  • 3. Attach Policies to the Role
  • Update the application with aws sync
  • Branch security measures
  • Final github workflow
  • Notify your team on deployment
  • Final Thoughts

You're launching an MVP and want to deploy you React app with minimal overhead, while limiting costs? In this tutorial, you’ll learn how to deploy a multi-environment React app to AWS S3.

The concepts that will follow have been implemented in the launchplate-react-terraform React template, which gives:

  • Lightning-fast Vite + SWC build setup
  • TailwindCSS + ShadCN for clean styling
  • Structured project with Domain-Driven Design (DDD)
  • Built-in multi-environment config support
  • CI/CD ready for AWS S3 + CloudFront quasi-free static hosting

The template leverages Terraform Infrastructure as Code to handle:

  • S3 buckets creation: A primary S3 bucket is created for each environment, as well as a secondary S3 bucket in production to ensure fault-tolerance.
  • CloudFront Caching: The S3-hosted websites are served exclusively through a CloudFront distribution, which caches content to improve load times, reduce latency, and minimize direct requests to the S3 buckets.
  • Multi-DNS providers support: DNS entries can be created either on Cloudflare or AWS Route53 DNS providers. SSL Certificates are managed using AWS ACM with DNS validation.
  • Optional Features: S3 buckets encryption (disabled by default), WAF protection (disabled by default)
  • Redirect from root to www: Enabled by default when using Cloudflare, based on Page Rules. This feature isn't supported when using Route53.

Want to dive into Terraform setup?

→ I’ll cover that in an upcoming tutorial—stay tuned.

Maintaining code standards

"If you want to go fast, if you want to get done quickly, if you want your code to be easy to write, make it easy to read" Robert C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship

To make sure your app is maintainable, it must be easy to read, which makes it easy to continue its development.

  1. Configure your IDE for linting and auto-formatting

In order to maintain styling standards during development, we can install the following extensions:

  • Tailwind CSS IntelliSense, which provides intellisense for tailwind classes.
  • ESLint extension and Prettier - Code formatter.

In VS Code settings (CTRL + ,):

  • Update "Editor: default formatter" to "Prettier - Code formatter".
  • Enable "Editor: Format on Save"
  1. Use Domain-Driven Design (DDD) + Clean Structure

Instead of dumping all components into a components/ folder and praying for clarity, we organize code by business feature, not technical and overly generic type.

Each business feature should have its own dedicated folder containing its components, hooks, etc, forming what we call a bounded context.

What is a bounded context in DDD? Imagine a user model. In the Auth feature, the model is an email/password pair. In the Admin feature, it's actually roles/permissions. Those business objects have the same name, but different responsibilities. So we isolate them in bounded contexts!

A bounded contextdefines the boundaries of your feature.

Project Structure

  • src/features: One folder per business capability (e.g., auth, theme, dashboard)
  • src/core: Reusable, app-agnostic components (e.g., Button, Layout, useDebounce)
  • src/shared: Project-wide reusable but project-specific logic/components
  • src/pages: Each page (e.g. home, 404, etc)
  • src/assets: Global images/icons
  1. Best practices when using DDD
  • Group files by feature Each feature manages its own components and states, including its own Zustand slices under features/X/store/X.slice.ts if you use Zustand.
  • Expose a minimal public API with a barrel export Each feature should have an ìndex.ts file that exports everything a feature exposes
  • Keep core/ generic Don't leak feature logic into core/
  • Shared = Project-Specific Utilities
  1. What are DDD anti-patterns?
  • Don't put business logic in core/
  • Do not use global stores in src/stores/ for state management, this breaks the DDD logic.
  • Don't overt-abstract The more flat the architecture is, the better. Keep It Simple, Stupid (KISS)
  1. Special cases: Use Open/Closed principle OCP from SOLID

Bonus: Open/Closed Principle for Reusability In features/theme/components, create a generic ThemeToggleButton, then create a ConnectedThemeToggle that plugs into Zustand or Redux. Boom – portable and extendable.

ThemeToggleButton is a closed, reusable component that takes callbacks.

Hosting: AWS S3 + CloudFront vs Netlify/Render/Cloudflare

A React app is just static files (HTML/CSS/JS), so why not host it on S3 for cents/month?

The dynamic content displayed in your website is fetched on client side when the user loads the page. That means you can just build your website and host the files on a static site hosting solution such as a quasi-free S3 bucket (HTTP).

S3 + CloudFront (Static Hosting + CDN + SSL)

  • Buy a domain name on a domain name registrar such as porkbun, Namecheap, etc. I recommend you to compare domain names prices by going to https://tld-list.com/
  • Delegate DNS to AWS Route53 or Cloudflare.DNS records are instructions that state how to handle request and associate IP addresses to the domain.You can use AWS Route 53 hosted zone for 0.50€/month, or Cloudflare, which provides DNS management and a web application firewall for free. There will however me more DNS configuration steps to serve the CloudFront distributions.The DNS provider of your choice will ask you to register their nameservers in your domain name registrar DNS settings.
  • Set up SSL via AWS Certificate Manager
  • Serve via CloudFront (CDN + HTTPS + cache)
  • Upload static build to S3 bucket

Alternatives (Cloudflare Pages / Netlify / Render)

They offer:

  • Git-based CI/CD
  • Easy custom domains
  • Serverless functions (like Cloudflare Workers)
  • Great free tiers for hobby projects

Which one to use? Depends on your needs and what each platform's ecosystem can provide:

  • Cloudflare Pages is arguably the best free-tier platform for static sites and Workers, which are serverless functions that always run at the edge, making them incredibly fast.
  • Firebase , made by Google, is great if you want backend capabilities like database and auth.

    Disclaimer: Firebase Authentication free tier (Spark Plan) has strong limitations when it comes to phone auth (limited to 10k verifications/month in the US, or to ~100-300 verifications/month in other countries) or to MFA, which is just plain unavailable.For authentication, there's not even to hesitate – Start with Supabase Cloud for a quick setup and a generous free tier (50k Montly Active Users (MAU) + $8/mo for 100k MAU). Then if needed, forget the subscription and self-host Supabase which is open-source. Since march 2025 Supabase introduced the Supabase UI Library base on tailwind CSS and ShadCN, which was the only missing element from this wholesome solution.

  • Render is strong if you need both static and backend services in one place.

Multi-environment deployment

When your web application starts having lots of traffic, you need to make a pre-release to ensure the changes do not break production. Real projects need:

  • dev: unstable, local testing
  • qa: for internal team testing
  • stg: user testing / UAT
  • prod: customer-facing production

Your configuration will usually vary between environments:

  • Different API URLs: When relying on APIs, The staging & dev environment will use the sandboxed version of the APIs.
  • Different feature toggles: You can use feature flags to disable a feature for various reasons: 1) A new feature is under active development and has been merged into the main codebase for agility's sake. 2) A published feature doesn't meet the customers' standards and has been disabled to be reworked.

We'll bake the configuration at build time. This means we build the app once per environment.

Vite variable support

Vite exposes env variables under import.meta.env object as strings automatically. These variables must be prefixed with VITE_ in .env files. We can use them like so:

&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-variable language_&quot;</span>&gt;<span class="hljs-variable language_">console</span>&lt;<span class="hljs-regexp">/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/</span>span&gt;(<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>import<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>.&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-property&quot;</span>&gt;meta&lt;<span class="hljs-regexp">/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;env&lt;/</span>span&gt;.&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-property&quot;</span>&gt;<span class="hljs-variable constant_">VITE_API_ENDPOINT</span>&lt;/span&gt;);
<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>// resolves to <span class="hljs-symbol">&amp;quot;</span>http://localhost:8080/<span class="hljs-symbol">&amp;quot;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>

Built-in Constants

By default, when running vite, NODE_ENV is set to developmentand .env.development is used. When running vite build, NODE_ENV is set to productionand .env.production is used.

Vite provides the following built-in constants:

  • import.meta.env.PROD: true if NODE_ENV equals to production
  • import.meta.env.DEV: true if NODE_ENV equals to development (always the opposite of import.meta.env.PROD)
  • import.meta.env.SSR: whether the app is running in the server.

Another concept from Vite is the Mode, which takes by default the same values ad NODE_ENV. You can overwrite the default mode by passing the --mode and retrieve it using import.meta.env.MODE. If you want to build your app for a staging mode, create a .env.staging and run vite build --mode staging.

Environment configurations

For our setup, define the following .env.[mode] files:

  • .env.development: VITE_API_URI="http://localhost:8080/"
  • .env.qa: VITE_API_URI="https://api-qa.example.com/"
  • .env.stg: VITE_API_URI="https://api-staging.example.com/"
  • .env.production VITE_API_URI="https://api.example.com/" Also add the line VITE_APP_VERSION=$npm_package_version to each .env files. This makes the app version specified in package.json accessible in the whole app through import.meta.env.VITE_APP_VERSION.

Add type definitions for custom env variables in src/vite-environment.d.ts to augment ImportMetaEnv:

src/vite-environment.<span class="hljs-property">d</span>.<span class="hljs-property">ts</span>
<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>/// <span class="hljs-symbol">&amp;lt;</span>reference types=<span class="hljs-symbol">&amp;quot;</span>vite/client<span class="hljs-symbol">&amp;quot;</span> /<span class="hljs-symbol">&amp;gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>/// <span class="hljs-symbol">&amp;lt;</span>reference types=<span class="hljs-symbol">&amp;quot;</span>vite/types/importMeta.d.ts<span class="hljs-symbol">&amp;quot;</span> /<span class="hljs-symbol">&amp;gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>/// <span class="hljs-symbol">&amp;lt;</span>reference types=<span class="hljs-symbol">&amp;quot;</span>vite-plugin-svgr/client<span class="hljs-symbol">&amp;quot;</span> /<span class="hljs-symbol">&amp;gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>/// <span class="hljs-symbol">&amp;lt;</span>reference types=<span class="hljs-symbol">&amp;quot;</span>vite/client<span class="hljs-symbol">&amp;quot;</span> /<span class="hljs-symbol">&amp;gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>

<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>interface<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;</span>ViteTypeOptions<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> {
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>// By adding this line, you can make the type of ImportMetaEnv strict<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>// to disallow unknown keys.<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>strictImportMetaEnv<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-built_in&quot;</span>&gt;</span>unknown<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
}

&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// eslint-disable-next-line unicorn/prevent-abbreviations&lt;/span&gt;</span>
<span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>interface<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;</span>ImportMetaEnv<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> {
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>readonly<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;</span>VITE_APP_VERSION<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-built_in&quot;</span>&gt;</span>string<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;</span>readonly<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-variable constant_&quot;</span>&gt;</span>VITE_API_URI<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>?: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-built_in&quot;</span>&gt;</span>string<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
  <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;</span>// more env variables...<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
}

&lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">interface</span>&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;<span class="hljs-title class_">ImportMeta</span>&lt;/span&gt; {
  &lt;span <span class="hljs-keyword">class</span>=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">readonly</span>&lt;<span class="hljs-regexp">/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;env&lt;/</span>span&gt;: <span class="language-xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;</span>ImportMetaEnvironment<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
}

In package.json, define one build command per environment:

&lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;start&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;lint&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;eslint .&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vitest run&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build<span class="hljs-punctuation">:</span>prod&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build<span class="hljs-punctuation">:</span>stg&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build --mode stg&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build<span class="hljs-punctuation">:</span>qa&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build --mode qa&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;

CI — GitHub Actions: Automated & Manual Workflows

Auto-Deploy by Branch Name

Source Branch Environment
main production
staging staging
qa qa
release/* qa
feature/* qa
bugfix/* qa

Listen to Github events by creating a workflow (.github/workflows/deploy.yml):

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;push:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;main&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;staging&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;release/*&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;feature/*&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;bugfix/*&amp;#x27;&lt;/span&gt;</span>

The push event is triggered on direct push to branch and when a PR is merged into branch.

Compute the target mode based on Github workflow github.* context:

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;env:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;MODE:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;(github.ref_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;main&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;prod&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;github.ref_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;staging&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;stg&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;)&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

The environment variable ${{ env.MODE }} takes the values 'prod' | 'staging' | 'qa'.

In the build stage, reference that environment variable:

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Build&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;project&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;npm&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;run&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build:${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;env.MODE&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

Finally we upload the built React app artifact, so that we can re-use it in the deployment job:

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Upload&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;artifacts&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/upload-artifact@v4&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;web-dist&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;./dist&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;retention-days:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;</span>

Manual Deploys with workflow_dispatch

Trigger deploys manually and select the environment from the GitHub UI.

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;workflow_dispatch:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;inputs:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;environment:&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;choice&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;options:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;prod&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;stg&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;qa&lt;/span&gt;</span>

Usually, multiple features are being developped at the same time. This means not everyone can push their single under development feature on the staging branch. To easily let developers deploy to that environment, we'll use workflow_disatch.

First, we need to listen the workflow_dispatch event:

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;workflow_dispatch:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;inputs:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;environment:&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;Select</span> <span class="hljs-string">the</span> <span class="hljs-string">environment</span> <span class="hljs-string">to</span> <span class="hljs-string">deploy</span> <span class="hljs-string">to&amp;#x27;&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;required:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;default:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;choice&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;options:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;qa&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;staging&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;production&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;push:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;main&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;staging&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;release/*&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;feature/*&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;bugfix/*&amp;#x27;&lt;/span&gt;</span>

Modify the target deployment environment specifically when the source event is workflow_dispatch:

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;env:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;ENVIRONMENT:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;github.event_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;workflow_dispatch&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;github.event.inputs.environment&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;(github.ref_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;main&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;production&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;github.ref_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;staging&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;staging&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;)&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

In your repo's Actions you can select the target environment to deploy to under Run workflow.

CD — Deploy to AWS (S3 + CloudFront)

We're now able to build our application with the target environment's configuration both in an automated and manual way with workflow dispatch, ensuring flexibility for our development team.

We'll host each environment in a separate S3 bucket to ensure isolation. In this tutorial we will assume that the S3 buckets already exist. The pipeline will only upload the newer app's distributable. All buckets should only be accessible through CloudFront to enable caching, so you'll have one CloudFront distribution per environment.

As the website is only static, a developer could deploy to the target environment from its computer by using the following scripts in package.json:

&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
    ...
    &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build<span class="hljs-punctuation">:</span>prod&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build --mode prod&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build<span class="hljs-punctuation">:</span>stg&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build --mode stg&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build<span class="hljs-punctuation">:</span>dev&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build --mode dev&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;vite build --mode prod&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;sync<span class="hljs-punctuation">:</span>prod&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;aws s3 sync dist s3<span class="hljs-punctuation">:</span><span class="hljs-comment">//prod-launchplate-react-primary &amp;amp;&amp;amp; aws cloudfront create-invalidation --distribution-id XXXX1 --paths &amp;#x27;/*&amp;#x27;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;</span>
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;sync<span class="hljs-punctuation">:</span>stg&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;aws s3 sync dist s3<span class="hljs-punctuation">:</span><span class="hljs-comment">//stg-launchplate-react-primary &amp;amp;&amp;amp; aws cloudfront create-invalidation --distribution-id XXXX2 --paths &amp;#x27;/*&amp;#x27;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;</span>
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;sync<span class="hljs-punctuation">:</span>dev&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;aws s3 sync dist s3<span class="hljs-punctuation">:</span><span class="hljs-comment">//dev-launchplate-react-primary &amp;amp;&amp;amp; aws cloudfront create-invalidation --distribution-id XXXX3 --paths &amp;#x27;/*&amp;#x27;&amp;quot;&lt;/span&gt;</span>
    &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;

Please replace the values with your buckets names and cloudfront distribution id.

We want the Github Workflow to perform these S3 synchronizations for us. To enable the workflow to communicate with AWS, we can leverage AWS OIDC Authentication, which doesn't require any exchange of secrets.

1. Set up AWS IAM OIDC Provider

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;# Create the OIDC provider <span class="hljs-keyword">in</span> AWS&lt;/span&gt;
aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;a031c46782e6e6c662c2c87c76da9aa62ccabd8e&amp;quot;&lt;/span&gt;

You get the following output:

&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;OpenIDConnectProviderArn&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;arn<span class="hljs-punctuation">:</span>aws<span class="hljs-punctuation">:</span>iam<span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span>AWS_ACCOUNT_ID<span class="hljs-punctuation">:</span>oidc-provider/token.actions.githubusercontent.com&amp;quot;&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;

2. Create an IAM Role for the Github Actions to assume

Create an IAM role named ReactLaunchplateGitHubActionsOIDCRole with the following trust policy:

&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Version&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;<span class="hljs-number">2012</span><span class="hljs-number">-10</span><span class="hljs-number">-17</span>&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Statement&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">[</span>&lt;/span&gt;
    &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Principal&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Federated&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;arn<span class="hljs-punctuation">:</span>aws<span class="hljs-punctuation">:</span>iam<span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span>&amp;lt;AWS_ACCOUNT_ID&amp;gt;<span class="hljs-punctuation">:</span>oidc-provider/token.actions.githubusercontent.com&amp;quot;&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;sts<span class="hljs-punctuation">:</span>AssumeRoleWithWebIdentity&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Condition&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;StringEquals&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
          &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;token.actions.githubusercontent.com<span class="hljs-punctuation">:</span>aud&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;sts.amazonaws.com&amp;quot;&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;StringLike&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
          &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;token.actions.githubusercontent.com<span class="hljs-punctuation">:</span>sub&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;repo<span class="hljs-punctuation">:</span>&amp;lt;GITHUB_ORG&amp;gt;/&amp;lt;REPO_NAME&amp;gt;<span class="hljs-punctuation">:</span>*&amp;quot;&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;
    &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">]</span>&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;

Replace <AWS_ACCOUNT_ID> with your AWS account ID, retrieved from the previous step. Replace <GITHUB_ORG> and <REPO_NAME> with your GitHub organization and repository names.

You can also create the role using aws CLI. Create a file oidc-provider-role.json containing the previous JSON, then run:

cd docs
aws iam create-role --role-name ReactLaunchplateGitHubActionsOIDCRole --assume-role-policy-document file://oidc-provider-role.json

As an output, we get:

&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
    &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Role&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Path&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;RoleName&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;ReactLaunchplateGitHubActionsOIDCRole&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Arn&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;arn<span class="hljs-punctuation">:</span>aws<span class="hljs-punctuation">:</span>iam<span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span>&amp;lt;AWS_ACCOUNT_ID&amp;gt;<span class="hljs-punctuation">:</span>role/ReactLaunchplateGitHubActionsOIDCRole&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        ...
    &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;

3. Attach Policies to the Role

We need to attach the necessary policies to this role (e.g., S3FullAccess, CloudFrontFullAccess, Route53FullAccess, etc.)

First, we need to create the policy. Create a file named provider-policy.json with the following content:

&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Version&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;<span class="hljs-number">2012</span><span class="hljs-number">-10</span><span class="hljs-number">-17</span>&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
  &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Statement&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">[</span>&lt;/span&gt;
    &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">[</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;s3<span class="hljs-punctuation">:</span>*&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;dynamodb<span class="hljs-punctuation">:</span>GetItem&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;dynamodb<span class="hljs-punctuation">:</span>PutItem&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;dynamodb<span class="hljs-punctuation">:</span>DeleteItem&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;dynamodb<span class="hljs-punctuation">:</span>UpdateItem&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;cloudfront<span class="hljs-punctuation">:</span>*&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;acm<span class="hljs-punctuation">:</span>DescribeCertificate&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;acm<span class="hljs-punctuation">:</span>ListCertificates&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;acm<span class="hljs-punctuation">:</span>ListTagsForCertificate&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;acm<span class="hljs-punctuation">:</span>GetCertificate&amp;quot;&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">]</span>&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
      &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Resource&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">[</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;arn<span class="hljs-punctuation">:</span>aws<span class="hljs-punctuation">:</span>s3<span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span>&amp;lt;PROJECT_NAME&amp;gt;-tfstate&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;arn<span class="hljs-punctuation">:</span>aws<span class="hljs-punctuation">:</span>s3<span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span>&amp;lt;PROJECT_NAME&amp;gt;-tfstate<span class="hljs-comment">/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:dynamodb:eu-west-3:YOUR_AWS_ACCOUNT_ID:table/&amp;lt;PROJECT_NAME&amp;gt;-tfstate-locks&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:s3:::*-&amp;lt;PROJECT_NAME&amp;gt;-primary&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:s3:::*-&amp;lt;PROJECT_NAME&amp;gt;-primary/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:s3:::*-&amp;lt;PROJECT_NAME&amp;gt;-failover&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:s3:::*-&amp;lt;PROJECT_NAME&amp;gt;-failover/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:s3:::*-&amp;lt;PROJECT_NAME&amp;gt;-log&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:s3:::*-&amp;lt;PROJECT_NAME&amp;gt;-log/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:cloudfront::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:distribution/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:acm:us-east-1:&amp;lt;AWS_ACCOUNT_ID&amp;gt;:certificate/*&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:cloudfront::***:origin-access-identity/*&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;s3:ListAllMyBuckets&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;acm:ListCertificates&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;cloudfront:ListDistributions&amp;quot;&lt;/span&gt;
      &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Resource&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;route53:GetHostedZone&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;route53:ListResourceRecordSets&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Resource&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:route53:::hostedzone/*&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;route53:ChangeResourceRecordSets&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Resource&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;arn:aws:route53:::hostedzone/*&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Effect&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Allow&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Action&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;route53:ListHostedZones&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;Resource&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;*&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
</span>

Modify oidc-provider-policy.json by replacing <PROJECT_NAME> with your project name (e.g., launchplate-react) and <AWS_ACCOUNT_ID> with your AWS account ID. Then run:

aws iam create-policy --policy-name ReactLaunchplateGitHubActionsOIDCRolePolicy --policy-document file://oidc-provider-policy.json

You get the following output:

&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
    &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Policy&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">{</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;PolicyName&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;ReactLaunchplateGitHubActionsOIDCRolePolicy&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        &lt;span class=<span class="hljs-string">&quot;hljs-attr&quot;</span>&gt;&amp;quot;Arn&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">:</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;arn<span class="hljs-punctuation">:</span>aws<span class="hljs-punctuation">:</span>iam<span class="hljs-punctuation">:</span><span class="hljs-punctuation">:</span>&amp;lt;AWS_ACCOUNT_ID&amp;gt;<span class="hljs-punctuation">:</span>policy/ReactLaunchplateGitHubActionsOIDCRolePolicy&amp;quot;&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">,</span>&lt;/span&gt;
        ...
    &lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-punctuation&quot;</span>&gt;<span class="hljs-punctuation">}</span>&lt;/span&gt;

Now than the policy is created, we can attach it to the role:

aws iam attach-role-policy --role-name ReactLaunchplateGitHubActionsOIDCRole --policy-arn arn:aws:iam::&amp;lt;AWS_ACCOUNT_ID&amp;gt;:policy/ReactLaunchplateGitHubActionsOIDCRolePolicy

Update the application with aws sync

The Github workflow authenticates to AWS usin the action aws-actions/configure-aws-credentials@v2 to which we pass the ARN of the role to assume, as well as the targeted AWS region.

In your repository's secrets, set AWS_ROLE_ARN to arn:aws:iam::<AWS_ACCOUNT_ID>:role/ReactLaunchplateGitHubActionsOIDCRole.

Add this job to the workflow:

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;env:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;aws_region:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;eu-west-3&lt;/span&gt;</span>
  

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;jobs:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;...&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;deploy:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Web&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Sync&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;to&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;S3&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;needs:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Checkout&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;repository&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/checkout@v4&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Setup&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;AWS&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;credentials&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;aws-actions/configure-aws-credentials@v2&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;role-to-assume:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;secrets.AWS_ROLE_ARN&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;aws-region:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;env.aws_region&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Download&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;artifacts&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/download-artifact@v4&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;web-dist&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;./dist&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Sync&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;to&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;S3&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;and&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;invalidate&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;CloudFront&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;cache&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;npm&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;run&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;sync:${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;env.MODE&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

Your website is now automatically synced on S3, and the cache is invalidated to make the changes instantly visible.

Branch security measures

To ensure the review process is followed, you should disallow pushing to the main branch by going to Settings > Rules > New branch ruleset.

  • Ruleset Name: Branch proteciton rules
  • Targets: Add target > Include default branch
  • Enable the following rules:
    • Restrict deletions
    • Require a pull request before merging. I do not recommend adding required approvals, as it makes the whole pull request process slower. I like the way Gitlab let us set approvals as required while allowing self-approval; The process of bypassing approvals become conscious and make a step back; Does my PR requires improvements or should I approve it already?

You can add a bypass setting to let certain roles bypass the ruleset:

Bypass

You can also bypass force push by going to Classic Branch protection rules > Branches > Add rule.

  • Set branch name pattern to *
  • Check "Require a pull request before merging"
  • Check "Require approvals" to 1 approval.
  • Under Rules applied to everyone including administrators, check Allow force pushes, then select Specify who can force push.

Final github workflow

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;AWS&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Deployment&lt;/span&gt;</span> 

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;workflow_dispatch:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;inputs:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;environment:&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;Select</span> <span class="hljs-string">the</span> <span class="hljs-string">environment</span> <span class="hljs-string">to</span> <span class="hljs-string">deploy</span> <span class="hljs-string">to&amp;#x27;&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;required:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;default:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;type:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;choice&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;options:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;qa&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;stg&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;prod&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;push:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;main&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;staging&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;release/*&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;feature/*&amp;#x27;&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;bugfix/*&amp;#x27;&lt;/span&gt;</span>

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;env:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;MODE:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;github.event_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;workflow_dispatch&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;github.event.inputs.environment&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;(github.ref_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;main&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;prod&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;github.ref_name&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;==&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;staging&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;stg&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;||&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;qa&amp;#x27;&lt;/span&gt;&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;)&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;jobs:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;build:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Lint,&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Test&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;amp;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Build&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;outputs:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;app_version:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;steps.get_version.outputs.app_version&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Checkout&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;repository&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/checkout@v4&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Setup&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Node&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/setup-node@v4&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;node-version:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;&amp;#x27;20&amp;#x27;&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-comment&quot;&gt;#</span> <span class="hljs-string">specify</span> <span class="hljs-string">a</span> <span class="hljs-string">version</span> <span class="hljs-string">if</span> <span class="hljs-string">needed&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Setup&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;pnpm&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;pnpm/action-setup@v4.1.0&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;version:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;latest&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;run_install:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Run&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Vitest&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;tests&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;pnpm&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;test&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;env:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;CI:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Get&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;App&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;version&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;id:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;get_version&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;|</span>
          <span class="hljs-string">APP_VERSION=$(node</span> <span class="hljs-string">-p</span> <span class="hljs-string">&amp;quot;require(&amp;#x27;./package.json&amp;#x27;).version&amp;quot;)</span>
          <span class="hljs-string">echo</span> <span class="hljs-string">&amp;quot;app_version=$APP_VERSION&amp;quot;</span> <span class="hljs-string">&amp;gt;&amp;gt;</span> <span class="hljs-string">$GITHUB_OUTPUT</span>
          <span class="hljs-string">echo</span> <span class="hljs-string">&amp;quot;App</span> <span class="hljs-attr">Version:</span> <span class="hljs-string">$APP_VERSION&amp;quot;</span>
<span class="hljs-string">&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Build&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;project&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;npm&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;run&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build:${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;env.MODE&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Upload&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;production-ready&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;files&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;id:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;deployment&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/upload-pages-artifact@v3&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;./dist&lt;/span&gt;</span>

  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;deploy:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Web&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Sync&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;to&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;S3&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;needs:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Checkout&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;repository&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/checkout@v4&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Setup&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;AWS&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;credentials&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;aws-actions/configure-aws-credentials@v2&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;role-to-assume:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;secrets.AWS_ROLE_ARN&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;aws-region:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;env.aws_region&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Download&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;artifacts&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;actions/download-artifact@v4&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;web-dist&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;path:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;./dist&lt;/span&gt;</span>

      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Sync&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;to&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;S3&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;and&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;invalidate&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;CloudFront&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;cache&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;npm&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;run&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;sync:${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;env.MODE&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

Notify your team on deployment

I find it really useful to get notified on Slack at the end of the workflow:

  • I instantly know when the Github Action has ended and can go check the results.
  • This confirms other team members that a new version has been deployed.

You can get Slack notifications by creating a Slack App that has an incoming webhook. To do that please check out Slack's tutorial.

Once done, you can add the following job to your Github workflow:

<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;slack:&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;Slack&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;needs:&lt;/span&gt;</span> [<span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;</span>, <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;deploy&lt;/span&gt;</span>]
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;if:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;always()&lt;/span&gt;</span>
  <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;</span>
    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;technote-space/workflow-conclusion-action@v3&lt;/span&gt;</span>

    <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;8398a7/action-slack@v3&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;status:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;custom&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;fields:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;workflow,job,commit,repo,ref,author,took&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;custom_payload:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;|</span>
          {
            <span class="hljs-attr">attachments:</span> [{
                <span class="hljs-attr">color:</span> <span class="hljs-string">&amp;#x27;$<span class="hljs-template-variable">{{ env.WORKFLOW_CONCLUSION }}</span></span><span class="hljs-string">&amp;#x27;</span> <span class="hljs-string">===</span> <span class="hljs-string">&amp;#x27;success&amp;#x27;</span> <span class="hljs-string">?</span> <span class="hljs-string">&amp;#x27;good&amp;#x27;</span> <span class="hljs-string">:</span> <span class="hljs-string">&amp;#x27;$<span class="hljs-template-variable">{{ env.WORKFLOW_CONCLUSION }}</span></span><span class="hljs-string">&amp;#x27;</span> <span class="hljs-string">===</span> <span class="hljs-string">&amp;#x27;failure&amp;#x27;</span> <span class="hljs-string">?</span> <span class="hljs-string">&amp;#x27;danger&amp;#x27;</span> <span class="hljs-string">:</span> <span class="hljs-string">&amp;#x27;warning&amp;#x27;</span>,
                <span class="hljs-attr">text:</span> <span class="hljs-string">`$</span>{<span class="hljs-string">process.env.AS_WORKFLOW</span>} <span class="hljs-string">—</span> <span class="hljs-string">*$<span class="hljs-template-variable">{{ env.WORKFLOW_CONCLUSION }}</span></span><span class="hljs-string">*</span>
                <span class="hljs-string">$</span>{<span class="hljs-string">process.env.AS_AUTHOR</span>} <span class="hljs-string">deployed</span> <span class="hljs-string">$</span>{<span class="hljs-string">process.env.AS_REPO</span>}<span class="hljs-string">@$</span>{<span class="hljs-string">process.env.AS_REF</span>} <span class="hljs-string">($</span>{<span class="hljs-string">process.env.AS_COMMIT</span>}<span class="hljs-string">)</span> <span class="hljs-string">in</span> <span class="hljs-string">$</span>{<span class="hljs-string">process.env.AS_TOOK</span>}
<span class="hljs-string">&lt;/span&gt;</span>
                <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;*Environment:*&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;$<span class="hljs-template-variable">{{&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;needs.build.outputs.app_version&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;}}</span></span><span class="hljs-string">`&lt;/span&gt;</span>
              <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;</span>}]<span class="hljs-string">&lt;/span&gt;</span>
          <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;</span>}<span class="hljs-string">&lt;/span&gt;</span>
      <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;env:&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;GITHUB_TOKEN:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;secrets.GITHUB_TOKEN&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>
        <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-attr&quot;&gt;SLACK_WEBHOOK_URL:&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;secrets.SLACK_WEBHOOK_URL&lt;/span&gt;</span> <span class="hljs-string">&lt;span</span> <span class="hljs-string">class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;</span>

Only set the slack webhook url in your Github repo's secrets, as secrets.GITHUB_TOKEN is automatically set.

Final Thoughts

You now have:

  • A modern React project template
  • DDD architecture best practices
  • CI/CD for multi-env deploys
  • S3 + CloudFront static site hosting
  • Peace of mind and bragging rights
  • Want to skip the boilerplate? Just clone the repo: launchplate-react-terraform ;)

Table of contents

  • Maintaining code standards
  • Hosting: AWS S3 + CloudFront vs Netlify/Render/Cloudflare
  • S3 + CloudFront (Static Hosting + CDN + SSL)
  • Alternatives (Cloudflare Pages / Netlify / Render)
  • Multi-environment deployment
  • CI — GitHub Actions: Automated & Manual Workflows
  • Auto-Deploy by Branch Name
  • Manual Deploys with workflow\dispatch
  • CD — Deploy to AWS (S3 + CloudFront)
  • 1. Set up AWS IAM OIDC Provider
  • 2. Create an IAM Role for the Github Actions to assume
  • 3. Attach Policies to the Role
  • Update the application with aws sync
  • Branch security measures
  • Final github workflow
  • Notify your team on deployment
  • Final Thoughts
vite react static websitedeploy react to s3multi environment deployment reactvite template react tailwindcloudfront s3 react

Comments

Be the first to comment!

More Articles

Create your own MCP servers

Create your own MCP servers

Discover how to design and build secure MCP servers using Typescript or Python.

fastmcp
cloudflare workers
typescripttypescript
Python
Streamable HTTP
17min read
Last updated 9 months ago
MCP servers - Introduction & Guide

MCP servers - Introduction & Guide

In this article, I explain how MCPs (Model Context Protocols) work and how to integrate them into your IDEs to connect external tools to your agent, providing it with context and levers for action to reduce redundant tasks -> Your IDE becomes a true all-in-one tool.

MCP
LLM
JSON
Stdio
SSE
Streamable HTTP
+18 more
6min read
Last updated 9 months ago
Cloud Email Microservices: A Guide to Using AWS Lambda and Cloudflare Workers

Cloud Email Microservices: A Guide to Using AWS Lambda and Cloudflare Workers

Deploy an email microservice on Lambda and handle queues — invoke from Cloudflare Workers or any Node.js backend

AWS Lambda
AWS SQS
remix.runremix.run
Cloudflare Workers
CloudFormation
SAM
15min read
Last updated 10 months ago
Best Practices for an Optimized Contact Page Design

Best Practices for an Optimized Contact Page Design

Build a Contact Page That Connects — and Blocks Spam

4min read
Last updated 10 months ago
7 Ways to Stop Form Spam in Remix / Node.js

7 Ways to Stop Form Spam in Remix / Node.js

Flag bot activity, use built-in rate limit APIs, prevent bounced emails

RemixRemix
Cloudflare Worker
Node.jsNode.js
yup
5min read
Last updated 10 months ago
Send and Receive Custom Domain Emails for Free

Send and Receive Custom Domain Emails for Free

Set up free professional email addresses like you@yourdomain.com without hosting a mail server or paying for Google Workspace. This guide shows how to receive emails using Forward Email and send as your custom domain via Gmail — fast, reliable, and 100% free.

Forward Email
Gmail
Google App Passwords
DNS
SPF
TLS
+1 more
Last updated 10 months ago
0

Offer

  • Contact
  • Skills

Resources

  • Blog
  • Newsletter
  • About

Legal

  • Privacy
  • Terms of service
  • Cookies policy
  • Update cookie consent
  • LinkedIn
  • GitHub

Gloweet © 2025. All rights reserved.

Smile, you're alive :)

2.0.0