<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ADITYA OKKE SUGIARSO</title>
    <description>The latest articles on DEV Community by ADITYA OKKE SUGIARSO (@adityaokke).</description>
    <link>https://dev.to/adityaokke</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1218514%2Faea003c6-7611-458a-a7ad-d1bf4cb99751.jpeg</url>
      <title>DEV Community: ADITYA OKKE SUGIARSO</title>
      <link>https://dev.to/adityaokke</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adityaokke"/>
    <language>en</language>
    <item>
      <title>Upload to Google Cloud Storage with GitHub Actions</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Thu, 25 Sep 2025 02:47:23 +0000</pubDate>
      <link>https://dev.to/adityaokke/upload-to-google-cloud-storage-with-github-actions-2e0a</link>
      <guid>https://dev.to/adityaokke/upload-to-google-cloud-storage-with-github-actions-2e0a</guid>
      <description>&lt;h2&gt;
  
  
  stack:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Google Cloud Storage&lt;/li&gt;
&lt;li&gt;Workload Identity Federation&lt;/li&gt;
&lt;li&gt;Service Accounts&lt;/li&gt;
&lt;li&gt;Static Site (Astro)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ref:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/google-github-actions/upload-cloud-storage" rel="noopener noreferrer"&gt;upload-cloud-storage marketplace actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=DMCi7WWTtX0" rel="noopener noreferrer"&gt;How to deploy Cloud Run services with GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=ZgVhU5qvK1M" rel="noopener noreferrer"&gt;How to use Github Actions with Google's Workload Identity Federation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=jNb2CFsHjsY" rel="noopener noreferrer"&gt;WIF for github actions using cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/actions/concepts/security/openid-connect" rel="noopener noreferrer"&gt;github token issuer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer-friendly.blog/blog/2025/02/17/how-to-deploy-static-site-to-gcp-cdn-with-github-actions/#github-runner-iam-binding" rel="noopener noreferrer"&gt;How to Deploy Static Site to GCP CDN with GitHub Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  intro:
&lt;/h2&gt;

&lt;p&gt;In this article, we'll create a GitHub Actions workflow that builds an Astro project and uploads the generated &lt;code&gt;dist/&lt;/code&gt; assets to Google Cloud Storage. We'll authenticate securely using Workload Identity Federation (WIF), eliminating the need for long-lived service account keys, and set everything up through the Google Cloud Console (web UI) from end to end. By the end, every push to repo can trigger a build and publish a static site to a GCS bucket.&lt;/p&gt;

&lt;h2&gt;
  
  
  Objective:
&lt;/h2&gt;

&lt;p&gt;Implement a GitHub Actions pipeline that uploads &lt;code&gt;dist/&lt;/code&gt; to Google Cloud Storage using Workload Identity Federation, employing one WIF pool/provider and a dedicated service account for each repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  step:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Create Workload Identity Federation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;open side menu, then choose Workload Identity Federation&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;choose Create Pool&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;fill the form in the first phase&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;fill the form on the second phase,
choose OIDC
for issuer use GitHub token issuer URL based on &lt;a href="https://docs.github.com/en/actions/concepts/security/openid-connect#understanding-the-oidc-token" rel="noopener noreferrer"&gt;GitHub OIDC&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyvjg94dscmdguls5vb5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyvjg94dscmdguls5vb5.png" alt="WIF pool form 2" width="800" height="682"&gt;&lt;/a&gt;&lt;br&gt;
keep the provider, we will use it later&lt;br&gt;
provider:&lt;code&gt;projects/111111111111/locations/global/workloadIdentityPools/github-action-pool/providers/github-action-provider&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fill form for the third phase
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// attribute mapping
google.subject=assertion.sub
attribute.repository=assertion.repository
// conditions
assertion.repository.startsWith("{{git_owner}}/")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;change &lt;code&gt;git_owner&lt;/code&gt; according yours&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;2. Create Service Accounts&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the side menu Service Accounts, then choose the Create service account button&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;fill form phase 1&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;fill form phase 2&lt;br&gt;
set permission to minimum, which is storage object user&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh2zal85r0rf8o2frapo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh2zal85r0rf8o2frapo.png" alt="service account form 2" width="507" height="408"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;fill form phase 3&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;3. Connect Service Account to Workload Identity Federation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;click pool we created before
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ax1d5997qpq5hkqocpu.png" alt="pool item" width="776" height="367"&gt;
&lt;/li&gt;
&lt;li&gt;a. choose Grant access button
&lt;/li&gt;
&lt;li&gt;b. choose Grant access using service account impersonation&lt;/li&gt;
&lt;li&gt;c. choose service account we created before&lt;/li&gt;
&lt;li&gt;d. in the Select principals, choose repository and fill your owner_name/repo_name
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzoy4qbqomydfq4r6s7fu.png" alt="connect service accounts" width="800" height="347"&gt;
&lt;/li&gt;
&lt;li&gt;e. choose dismiss, because we dont need the file&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;f. Wait a minute to see service accounts loading, and you can see the principal will be loaded, you can add another principal for other repo by repeat step on 3.a
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6b1dkvw7zy4uuikaonmd.png" alt="result connect service accounts" width="800" height="246"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Create GitHub action workflow&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choose New workflow button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0r0gmm300q65ab3f4uc.png" alt="new workflow button" width="554" height="209"&gt;
&lt;/li&gt;
&lt;li&gt;choose set up a workflow yourself&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;in this article, our purpose are &lt;a href="https://github.com/google-github-actions/upload-cloud-storage" rel="noopener noreferrer"&gt;upload&lt;/a&gt; &lt;code&gt;dist&lt;/code&gt; directory to Google Cloud storage
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Sample workflow for building and deploying an Static site to Google Cloud Storage
name: Deploy static site to Google Cloud Storage

on:
  # Runs on pushes targeting the default branch
  push:
    branches: ["main"]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write    # needed for WIF
      contents: read
    env:
      BUCKET: ${{ vars.GCS_BUCKET_NAME }}
      REGION: ${{ vars.GCS_BUCKET_REGION }}
    steps:
      - id: 'checkout'
        uses: 'actions/checkout@v4'

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - run: npm ci
      - run: npm run build

      # Authenticate to GCP (WIF)
      - id: 'auth'
        name: Auth to Google Cloud (WIF)
        uses: 'google-github-actions/auth@v3'
        with:
          workload_identity_provider: ${{ secrets.GCP_WIF_PROVIDER }}
          service_account: ${{ secrets.GCP_SA_EMAIL }}

      - id: 'upload-folder'
        name: 'Upload folder to GCS'
        uses: 'google-github-actions/upload-cloud-storage@v3'
        with:
          path: dist/
          destination: ${{ env.BUCKET }}
          parent: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;put workflow code above on the editor&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;choose Commit changes... button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnty5ul10bgl69r3fzet8.png" alt="commit changes..." width="800" height="211"&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;choose Commit changes button&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstny1b14n13iwixkzlwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fstny1b14n13iwixkzlwc.png" alt="commit changes" width="548" height="640"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;set up the env and secret variable&lt;br&gt;
because we dont set target environment, we will use respository scope&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jjlwmysaafnnv7pkzsa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7jjlwmysaafnnv7pkzsa.png" alt="set secarets" width="800" height="650"&gt;&lt;/a&gt;&lt;br&gt;
set 2 secrets variable we will used on the workflow&lt;/p&gt;

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

&lt;p&gt;and &lt;/p&gt;

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

&lt;p&gt;after we set the secrets, now set the variables&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvocstfo7nm9jwwvlkac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjvocstfo7nm9jwwvlkac.png" alt="set variables" width="800" height="467"&gt;&lt;/a&gt;&lt;br&gt;
set bucket name and region name based on your bucket info&lt;/p&gt;

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

&lt;p&gt;set bucket name&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiduk1itfy3miq0b65k1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiduk1itfy3miq0b65k1k.png" alt="bucket name" width="508" height="582"&gt;&lt;/a&gt;&lt;br&gt;
set bucket region name&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk51cehc0tx96odyiid0q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk51cehc0tx96odyiid0q.png" alt="region name" width="471" height="608"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Push commit to repo or run workflow manually&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foc0c2k6b0svsdg3revvy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foc0c2k6b0svsdg3revvy.png" alt="manual run workflow" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>googlecloud</category>
      <category>gcs</category>
    </item>
    <item>
      <title>Send Email using aws-sdk-v2.sesv2 on golang</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Wed, 20 Aug 2025 15:41:20 +0000</pubDate>
      <link>https://dev.to/adityaokke/send-email-using-aws-v2sesv2-on-golang-4dfb</link>
      <guid>https://dev.to/adityaokke/send-email-using-aws-v2sesv2-on-golang-4dfb</guid>
      <description>&lt;h2&gt;
  
  
  stack:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;go&lt;/li&gt;
&lt;li&gt;aws&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ref:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/aws/aws-sdk-go-v2" rel="noopener noreferrer"&gt;https://github.com/aws/aws-sdk-go-v2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/awsdocs/aws-doc-sdk-examples" rel="noopener noreferrer"&gt;https://github.com/awsdocs/aws-doc-sdk-examples&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/ses/latest/dg/send-an-email-from-console.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/ses/latest/dg/send-an-email-from-console.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.email/docs/integrations/aws-ses" rel="noopener noreferrer"&gt;https://react.email/docs/integrations/aws-ses&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  prep:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/adityaokke/creating-iam-access-keys-for-awsaccesskeyid-and-awssecretaccesskey-34i3"&gt;setup access key to use aws sdk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  step:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. initiate ses service on aws&lt;/strong&gt;&lt;br&gt;
choose your region on aws&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;2. create identities to use sandbox feature from aws ses&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;p&gt;fill form&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6fnqc8om4335e2mgqzv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi6fnqc8om4335e2mgqzv.png" alt="identity details" width="800" height="365"&gt;&lt;/a&gt;&lt;br&gt;
create another identity for &lt;code&gt;user@gmail.com&lt;/code&gt;&lt;br&gt;
and you should get &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegurnruusbl1dqxenqmd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegurnruusbl1dqxenqmd.png" alt="identities" width="800" height="113"&gt;&lt;/a&gt;&lt;br&gt;
finally you just need to verify your email by click link verification on the email inbox&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.Initialize Project&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ mkdir ~/helloaws
$ cd ~/helloaws
$ go mod init helloaws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.Add SDK Dependencies&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ go get github.com/aws/aws-sdk-go-v2/aws
$ go get github.com/aws/aws-sdk-go-v2/config
$ go get github.com/aws/aws-sdk-go-v2/service/sesv2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.Write Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
    "context"
    "fmt"
    "log"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/service/sesv2"
    "github.com/aws/aws-sdk-go-v2/service/sesv2/types"
)

func main() {
    // Using the SDK's default configuration, load additional config
    // and credentials values from the environment variables, shared
    // credentials, and shared configuration files
    cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("ap-southeast-1"))
    if err != nil {
        log.Fatalf("unable to load SDK config, %v", err)
    }

    // Build the request with its input parameters
    resp, err := svc.SendEmail(context.TODO(), &amp;amp;sesv2.SendEmailInput{
        FromEmailAddress: aws.String("admin@gmail.com"),
        Destination: &amp;amp;types.Destination{
            ToAddresses: []string{"user@gmail.com"},
        },
        Content: &amp;amp;types.EmailContent{
            Simple: &amp;amp;types.Message{
                Subject: &amp;amp;types.Content{
                    Data: aws.String("Test Email"),
                },
                Body: &amp;amp;types.Body{
                    Text: &amp;amp;types.Content{
                        Data: aws.String("This is a test email sent using AWS SES."),
                    },
                },
            },
        },
    })
    if err != nil {
        fmt.Printf("Error sending email: %v\n", err)
    }

    fmt.Printf("Email sent successfully, message ID: %s\n", *resp.MessageId)
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Check &lt;a href="mailto:user@gmail.com"&gt;user@gmail.com&lt;/a&gt; inbox or spam for the test email&lt;/strong&gt;&lt;/p&gt;

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

</description>
      <category>go</category>
      <category>aws</category>
    </item>
    <item>
      <title>Creating IAM Access Keys for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Wed, 20 Aug 2025 15:33:28 +0000</pubDate>
      <link>https://dev.to/adityaokke/creating-iam-access-keys-for-awsaccesskeyid-and-awssecretaccesskey-34i3</link>
      <guid>https://dev.to/adityaokke/creating-iam-access-keys-for-awsaccesskeyid-and-awssecretaccesskey-34i3</guid>
      <description>&lt;h2&gt;
  
  
  stack:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;aws&lt;/li&gt;
&lt;li&gt;go&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ref:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  intro:
&lt;/h2&gt;

&lt;p&gt;IAM user access keys consist of two parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access key ID (for example: AKIAIOSFODNN7EXAMPLE)&lt;/li&gt;
&lt;li&gt;Secret access key (for example: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You must use both the access key ID and the secret access key together to authenticate requests made through the AWS SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  step:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Create IAM user&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Open the IAM Dashboard in the AWS Management Console. In the left navigation pane, choose Users.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;click Create user button&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;set user name&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;on set permissions, create group to attach the policies&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Set a group name and choose permission policies. 
These policies usually provide full access per AWS service. If you need more fine-grained control, you can create your own custom policies by selecting the Create policy button.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;after that, review and select Create User button&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;2. Create access key&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;choose Create access key&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2bglh79geg2fzpw4nq8s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2bglh79geg2fzpw4nq8s.png" alt="create access key" width="800" height="163"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;choose local code&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;fill any meaningful name then choose create key&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;if sucess, you will have &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; and &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; to put on .env&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;now you can put both key on .env. AWS SDK will automatically detect the key on .env
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS_ACCESS_KEY_ID=AKIAZF************
AWS_SECRET_ACCESS_KEY=utiKWhMNy***********************************
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aws</category>
      <category>go</category>
    </item>
    <item>
      <title>Create Golang private package</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Fri, 15 Aug 2025 05:13:17 +0000</pubDate>
      <link>https://dev.to/adityaokke/create-golang-private-package-15bn</link>
      <guid>https://dev.to/adityaokke/create-golang-private-package-15bn</guid>
      <description>&lt;h2&gt;
  
  
  note:
&lt;/h2&gt;

&lt;p&gt;this article based on chatGPT generated content and already tested by me&lt;/p&gt;

&lt;h2&gt;
  
  
  step:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Host your repo somewhere private&lt;/strong&gt;&lt;br&gt;
E.g. GitHub/GitLab/Bitbucket private repo at&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# git
git@github.com:your-org/private-pkg.git

# bitbucket
git@bitbucket.org:your-org/private-pkg.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Initialize the module&lt;/strong&gt;&lt;br&gt;
In your local checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd private-pkg
go mod init github.com/your-org/private-pkg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a go.mod with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
module github.com/your-org/private-pkg

go 1.21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Write your code&lt;/strong&gt;&lt;br&gt;
Example &lt;code&gt;foo.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package privatepkg

// Hello returns a greeting.
func Hello(name string) string {
    return "Hello, " + name
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit &amp;amp; push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add .
git commit -m "initial private-pkg module"
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Tag a version (Semantic Versioning)&lt;/strong&gt;&lt;br&gt;
Decide on your first release, e.g. v0.1.0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git tag v0.1.0
git push origin v0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go tooling will recognize that tag as the module’s v0.1.0 version.&lt;br&gt;
&lt;strong&gt;5. Tell Go which repos are private&lt;/strong&gt;&lt;br&gt;
On any machine that will consume your private module, set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# mark your entire Bitbucket org as private
go env -w GOPRIVATE=bitbucket.org/your-org/*

# mark your entire Github org as private
go env -w GOPRIVATE=github.com/your-org/*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://go.dev/ref/mod#private-modules" rel="noopener noreferrer"&gt;GOPRIVATE&lt;/a&gt; - list of glob patterns of module path prefixes that should be considered private. Acts as a default value for &lt;code&gt;GONOPROXY&lt;/code&gt; and &lt;code&gt;GONOSUMDB&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6. Consume the module in another project&lt;/strong&gt;&lt;br&gt;
In your client repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod init example.com/your-app
go get github.com/your-org/private-pkg@v0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Import and use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import "github.com/your-org/private-pkg"

func main() {
    fmt.Println(privatepkg.Hello("World"))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go build

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Releasing updates&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make changes in &lt;code&gt;private-pkg&lt;/code&gt;, bump code.&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;go.mod&lt;/code&gt; if you need to require newer dependencies.&lt;/li&gt;
&lt;li&gt;Commit and &lt;code&gt;git tag v0.2.0&lt;/code&gt; (or &lt;code&gt;v1.0.0&lt;/code&gt; when you make a breaking change).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push origin main --tags&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In your app: &lt;code&gt;go get github.com/your-org/private-pkg@v0.2.0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
    </item>
    <item>
      <title>Define a Self-Referential Many-to-Many Model in GORM</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Thu, 14 Aug 2025 20:06:23 +0000</pubDate>
      <link>https://dev.to/adityaokke/create-self-referential-many2many-on-gorm-1g72</link>
      <guid>https://dev.to/adityaokke/create-self-referential-many2many-on-gorm-1g72</guid>
      <description>&lt;h2&gt;
  
  
  stack:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Language: Go&lt;/li&gt;
&lt;li&gt;ORM: GORM&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ref:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gorm.io/docs/many_to_many.html#Customize-JoinTable" rel="noopener noreferrer"&gt;custom join table&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gorm.io/docs/many_to_many.html#Self-Referential-Many2Many" rel="noopener noreferrer"&gt;self referential table&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gorm.io/docs/many_to_many.html#Self-Referential-Many2Many" rel="noopener noreferrer"&gt;custom foreign key on join table&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  case:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Self-referential table&lt;/li&gt;
&lt;li&gt;Association type: many-to-many&lt;/li&gt;
&lt;li&gt;Join table has additional columns beyond the two foreign keys&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  step:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Define Item
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type Item struct {
    ID         string `gorm:"primaryKey; type:VARCHAR(36); NOT NULL"`
    Name       string `gorm:"type:VARCHAR(100); NOT NULL"`
    Price      int32  `gorm:"type:INT; NOT NULL"`
    ChildItems []Item `gorm:"many2many:parent_child_items;foreignKey:ID;joinForeignKey:ParentID;References:ID;joinReferences:ChildID"`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which creates join table: parent_child_items&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;foreign key: &lt;code&gt;parent_id&lt;/code&gt;, reference: &lt;code&gt;items.id&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;foreign key: &lt;code&gt;client_id&lt;/code&gt;, reference: &lt;code&gt;items.id&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Define ParentChildItem
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type ParentChildItem struct {
    ParentID string `gorm:"primaryKey; type:VARCHAR(36); NOT NULL"`
    ChildID  string `gorm:"primaryKey; type:VARCHAR(36); NOT NULL"`
    CreatedAt time.Time
    DeletedAt gorm.DeletedAt
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define this because the &lt;code&gt;parent_child_items&lt;/code&gt; table stores more than just &lt;code&gt;parent_id&lt;/code&gt; and &lt;code&gt;child_id&lt;/code&gt;. It’s a custom association table with extra fields (&lt;code&gt;CreatedAt&lt;/code&gt;, &lt;code&gt;DeletedAt&lt;/code&gt;), not a pure join table.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run this code on startup
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    db.AutoMigrate(&amp;amp;entity.Item{}, &amp;amp;entity.ParentChildItem{})
    err := db.SetupJoinTable(&amp;amp;entity.Item{}, "ChildItems", &amp;amp;entity.ParentChildItem{})
    if err != nil {
        panic(fmt.Sprintf("Failed to setup join table: %v", err))
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>go</category>
    </item>
    <item>
      <title>Semantic search on go + python</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Tue, 22 Jul 2025 14:28:44 +0000</pubDate>
      <link>https://dev.to/adityaokke/semantic-search-on-go-python-44p7</link>
      <guid>https://dev.to/adityaokke/semantic-search-on-go-python-44p7</guid>
      <description>&lt;p&gt;stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;golang (gorm, pgvector-go)&lt;/li&gt;
&lt;li&gt;python (langchain)&lt;/li&gt;
&lt;li&gt;docker (docker compose)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ref:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://python.langchain.com/docs/tutorials/retrievers/" rel="noopener noreferrer"&gt;https://python.langchain.com/docs/tutorials/retrievers/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pgvector" rel="noopener noreferrer"&gt;https://github.com/pgvector&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pgvector/pgvector-go" rel="noopener noreferrer"&gt;https://github.com/pgvector/pgvector-go&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  setup pgvector
&lt;/h2&gt;

&lt;p&gt;using docker-compose to setup postgres + pgvector&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '2'
services:
  db-postgres:
    image: postgres
    restart: always
    user: postgres_user
    environment:
      POSTGRES_USER: postgres_user
      POSTGRES_PASSWORD: postgres_pass
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: [ "CMD", "pg_isready" ]
      interval: 10s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"
    networks:
      - default
volumes:
  db-data:
networks:
  default:
    name: wawancara_namespace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  separate embedding data and the content
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package models

import (
    "time"

    "github.com/pgvector/pgvector-go"
)

// A) Core item
type Item struct {
    ID          int           `gorm:"primaryKey"`
    Name        string        `gorm:"not null"`
    Description string        `gorm:"type:text"`
}

// B) Embeddings table
type ItemVector struct {
    ID         int              `gorm:"primaryKey"`
    ItemID     int              `gorm:"not null;index"`
    Embedding  pgvector.Vector  `gorm:"type:vector(1536);not null"`
    Item       *Item
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generate embed data
&lt;/h2&gt;

&lt;p&gt;as per langchain documentation about semantic search, i only implement splitter and embedding method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def text_embed(content: str) -&amp;gt; List[List[np.float32]]:
    # 1) Split into chunks
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,
        chunk_overlap=200,
        add_start_index=True,
    )
    chunks = splitter.split_text(content)

    # 2) Embed each chunk
    embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

    all_vectors: List[List[float]] = []
    for chunk in chunks:
        # 3a) Get a 64‑bit embedding from OpenAI
        vec64: List[float] = embeddings.embed_query(chunk)
        # 3b) Cast down to 32‑bit and convert to plain Python list
        vec32 = np.array(vec64, dtype=np.float32).tolist()
        all_vectors.append(vec32)

    return all_vectors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;need to make it to float32 here, because pgvector only accept float32&lt;/p&gt;

&lt;h2&gt;
  
  
  save data to pgvector
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func SaveChunkEmbeddings(db *gorm.DB, cvRankerID int, allVectors [][]float32) error {
    // Bulk insert version:
    items := make([]models.ItemVector, len(allVectors))
    for i, vec := range allVectors {
        items[i] = models.ItemVector{
            ItemID:     cvRankerID,
            ChunkIndex: i,
            Embedding:  pgvector.NewVector(vec),
        }
    }

    if err := db.CreateInBatches(items, 100).Error; err != nil {
        log.Printf("bulk insert embeddings failed: %v", err)
        return err
    }
    return nil
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now we have data vector for our item&lt;/p&gt;

&lt;h2&gt;
  
  
  Get embed data from query
&lt;/h2&gt;

&lt;p&gt;When user search something, we just need to get vector of the query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def query_embed(content: str) -&amp;gt; List[np.float32]:
    """
    Embed a single query string into a 32-bit vector.
    """
    embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
    vec64: List[float] = embeddings.embed_query(content)
    vec32 = np.array(vec64, dtype=np.float32).tolist()
    return vec32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then use the vector in our vector table search&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func Top5ItemsByEmbedding(db *gorm.DB, embedding []float32) ([]ItemVector, error) {
    var results []ItemVector

    vec := pgvector.NewVector(embedding)

    err := db.
        Model(&amp;amp;entity.ItemVector{}).
Join("Item")
        Select("item_id, MIN(embedding &amp;lt;-&amp;gt; ?) AS score", vec).
        Group("item_id").
        Order("score ASC").
        Limit(5).
        Scan(&amp;amp;results).Error

    return results, err
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>How to secure internal-authorization header</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Sun, 06 Oct 2024 11:17:43 +0000</pubDate>
      <link>https://dev.to/adityaokke/how-to-secure-internal-authorization-header-1o0b</link>
      <guid>https://dev.to/adityaokke/how-to-secure-internal-authorization-header-1o0b</guid>
      <description>&lt;p&gt;stack:&lt;br&gt;
graphql&lt;br&gt;
nginx&lt;br&gt;
docker-compose&lt;/p&gt;

&lt;p&gt;request flow diagram&lt;/p&gt;

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

&lt;p&gt;nginx config to allowlist request from other service by using their internal IP&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 7000;
    allow 10.101.0.01;
    # internal IP of service A
    deny all;


    location / {
        proxy_pass http://api-project-B:7000;
        # api-project-B is service name on docker-compose
        # 7000 is port used by the application on api-project-B service
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if your user service and gateway service on 1 instance, and you need internal-authorization header implemented on user service, you can deny access to the user graphql URL so the client can only access to user graphql through gateway&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 443 ssl http2;

    location / {
        proxy_pass http://api-gateway:5000;
    }
    # deny access to /user/graphql from client
    location /user/graphql {
        deny all;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Simple docker deploy on gcp</title>
      <dc:creator>ADITYA OKKE SUGIARSO</dc:creator>
      <pubDate>Sun, 22 Sep 2024 05:10:18 +0000</pubDate>
      <link>https://dev.to/adityaokke/simple-docker-deploy-on-gcp-39hl</link>
      <guid>https://dev.to/adityaokke/simple-docker-deploy-on-gcp-39hl</guid>
      <description>&lt;p&gt;Setup git on gcp&lt;br&gt;
Set up personal SSH keys on Linux&lt;br&gt;
Use Git cmd to manage repo&lt;br&gt;
Install docker so we can use docker compose&lt;br&gt;
Create SSL certificate&lt;br&gt;
GCP IP Address&lt;br&gt;
Interact with postgres docker container&lt;br&gt;
Install cmake to accommodate makefile&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup git on gcp
&lt;/h2&gt;

&lt;p&gt;To install Git on Linux, follow these steps:&lt;/p&gt;

&lt;p&gt;Update the package index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install Git:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git --version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the installed Git version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up personal SSH keys on Linux
&lt;/h2&gt;

&lt;p&gt;To clone a repo on bitbucket, you need to set up personal SSH keys&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-linux/#Install-OpenSSH-on-Linux" rel="noopener noreferrer"&gt;Install OpenSSH on your device.&lt;/a&gt;
For Debian, Ubuntu, Linux Mint, and other Debian-based distributions:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt install openssh-client
ssh -V
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-linux/#Start-the-SSH-agent" rel="noopener noreferrer"&gt;Start the SSH Agent.&lt;/a&gt;
To check if SSH agent is running:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ps -ax | grep ssh-agent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;output should be like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;19998 ??         0:00.20 /usr/bin/ssh-agent -l
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start the agent, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eval $(ssh-agent)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may need to add this command to your &lt;code&gt;~/.bashrc&lt;/code&gt;, &lt;code&gt;~/.zshrc&lt;/code&gt;, &lt;code&gt;~/.profile&lt;/code&gt;, or equivalent shell configuration file. &lt;br&gt;
use&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nano ~/.profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then put &lt;code&gt;eval $(ssh-agent)&lt;/code&gt; on it&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-linux/#Create-an-SSH-key-pair" rel="noopener noreferrer"&gt;Create an SSH key pair.&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ~
ssh-keygen -t ed25519 -b 4096 -C "{username@emaildomain.com}" -f ~/.ssh/{ssh-key-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;{&lt;a href="mailto:username@emaildomain.com"&gt;username@emaildomain.com&lt;/a&gt;} is the email address associated with the Bitbucket Cloud account, such as your work email account.&lt;/p&gt;

&lt;p&gt;{ssh-key-name} is the output filename for the keys. We recommend using a identifiable name such as bitbucket_work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once complete, ssh-keygen will output two files:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{ssh-key-name}&lt;/code&gt; — the private key.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{ssh-key-name}.pub&lt;/code&gt; — the public key.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-linux/#Add-your-key-to-the-SSH-agent" rel="noopener noreferrer"&gt;Add your key to the SSH agent.&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-add ~/.ssh/{ssh-key-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To ensure the correct SSH key is used when connecting to Bitbucket, update or create your SSH configuration file (&lt;code&gt;~/.ssh/config&lt;/code&gt;) with the following settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host bitbucket.org
  AddKeysToAgent yes
  IdentityFile ~/.ssh/{ssh-key-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-linux/#Provide-Bitbucket-Cloud-with-your-public-key" rel="noopener noreferrer"&gt;Provide Bitbucket Cloud with your public key.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/set-up-personal-ssh-keys-on-linux/#Check-that-your-SSH-authentication-works" rel="noopener noreferrer"&gt;Check that your SSH authentication works.&lt;/a&gt;
To test that the SSH key was added successfully:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -T git@bitbucket.org
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;output should be like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;authenticated via ssh key.

You can use git to connect to Bitbucket. Shell access is disabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use Git cmd to manage repo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;clone the repo using clone command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone git@bitbucket.org:{org-name}/{repo-name}.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;above is just sample, you can get the command from clone button in bitbucket repository&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;move to repository folder using cd command
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd {repo-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;use git status to check changes and your current branch
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;change current branch using git checkout
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout {branch-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;update your local branch and local commit history according to remote
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git fetch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;pull the changes from remote using git pull
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install docker so we can use docker compose
&lt;/h2&gt;

&lt;p&gt;full step to install docker on debian are in &lt;a href="https://docs.docker.com/engine/install/debian/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;summarize steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run the following command to uninstall all conflicting packages:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Set up Docker's &lt;code&gt;apt&lt;/code&gt; repository.
the step are divided by 2:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;First, Add Docker's official GPG key, run these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, Add the repository to Apt sources, run these commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release &amp;amp;&amp;amp; echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null
sudo apt-get update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Docker packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that the Docker Engine installation is successful by running the hello-world image.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create SSL certificate
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://netshopisp.medium.com/how-to-install-certbot-on-debian-11-for-lets-encrypt-ssl-768562a8de0" rel="noopener noreferrer"&gt;Step-by-step instructions to install Certbot on linux&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install snapd&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update
sudo apt install snapd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update snapd&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo snap install core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove previous Certbot packages&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get remove certbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install Certbot&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo snap install --classic certbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once certbot is installed, execute the following command to ensure the &lt;code&gt;certbot&lt;/code&gt; command can be run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ln -s /snap/bin/certbot /usr/bin/certbot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate SSL Certificate with Let’s Encrypt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo certbot certonly --manual --preferred-challenges dns -d *.domain.com -d domain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you will get acme value to put on your DNS, in above example, you will get 2 acme that need to put orderly one by one, below are sample DNS TXT acme&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please deploy a DNS TXT record under the name:

_acme-challenge.domain.com.

with the following value:

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

&lt;/div&gt;



&lt;p&gt;you need to put value above to your DNS dashboard, my DNS dashboard are look like this&lt;/p&gt;

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

&lt;p&gt;after you add the record(s), you can enter on the cmd to continue to the next acme or the next step&lt;/p&gt;

&lt;p&gt;Success generated SSL will have this info&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/domain.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/domain.com/privkey.pem
This certificate expires on 2024-12-17.
These files will be updated when the certificate renews.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;use both according your approach how to apply SSL on your domain&lt;/p&gt;

&lt;h2&gt;
  
  
  GCP IP Address
&lt;/h2&gt;

&lt;p&gt;Upgrade your internal and external IP address on GCP to static, so it will not changing when the instance got restart&lt;/p&gt;

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

&lt;p&gt;register your external address to your dns&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Interact with postgres docker
&lt;/h2&gt;

&lt;p&gt;connect to docker cli&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo docker exec -it {docker-container-name} sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;connect to postgres cli&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql -h localhost -U {user-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;create database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE DATABASE {database-name};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;connect to the database&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\c {database-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or you can immediate put the database name as parameter while connect to postgres cli&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql -h localhost -U {user-name} -d {database-name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install cmake to accommodate makefile
&lt;/h2&gt;

&lt;p&gt;Update the Debian System Before Installing CMake&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt update &amp;amp;&amp;amp; sudo apt upgrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install CMake via APT Command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install cmake
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>googlecloud</category>
      <category>docker</category>
      <category>go</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
