From 815a5aeb1c45e18c59faa53e536e2ee8a71e5c1a Mon Sep 17 00:00:00 2001
From: Luiz Carlos <argentinaluiz@gmail.com>
Date: Thu, 14 Dec 2023 18:44:47 -0300
Subject: [PATCH] Montagem do CI com Gihtub Action

---
 .github/workflows/ci-cd.yaml                | 122 ++++++++++++++++++++
 docker-compose.ci.yaml                      |   3 +
 src/@types/supertest.d.ts                   |   2 +-
 src/core/shared/infra/config.ts             |   9 +-
 test/categories/delete-category.e2e-spec.ts |   2 +
 test/categories/get-category.e2e-spec.ts    |   2 +
 test/categories/list-categories.e2e-spec.ts |   2 +
 test/categories/update-category.e2e-spec.ts |   4 +
 8 files changed, 143 insertions(+), 3 deletions(-)
 create mode 100644 .github/workflows/ci-cd.yaml

diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml
new file mode 100644
index 0000000..eeb1f96
--- /dev/null
+++ b/.github/workflows/ci-cd.yaml
@@ -0,0 +1,122 @@
+name: CI/CD
+
+# This workflow uses actions that are not certified by GitHub.
+# They are provided by a third-party and are governed by
+# separate terms of service, privacy policy, and support
+# documentation.
+
+on:
+  push:
+    branches: ['main']
+    # Publish semver tags as releases.
+    tags: ['v*.*.*']
+  pull_request:
+    branches: ['main']
+
+env:
+  # Use docker.io for Docker Hub if empty
+  REGISTRY: ghcr.io
+  # github.repository as <account>/<repo>
+  IMAGE_NAME: ${{ github.repository }}
+  FOLDER_NAME: ${{ github.event.repository.name }}-app
+
+jobs:
+  ci:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+      packages: write
+      # This is used to complete the identity challenge
+      # with sigstore/fulcio when running outside of PRs.
+      id-token: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v3
+
+      # Install the cosign tool except on PR
+      # https://github.com/sigstore/cosign-installer
+      - name: Install cosign
+        if: github.event_name != 'pull_request'
+        uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1
+        with:
+          cosign-release: 'v2.1.1'
+
+      # Set up BuildKit Docker container builder to be able to build
+      # multi-platform images and export cache
+      # https://github.com/docker/setup-buildx-action
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
+
+      # Login against a Docker registry except on PR
+      # https://github.com/docker/login-action
+      - name: Log into registry ${{ env.REGISTRY }}
+        if: github.event_name != 'pull_request'
+        uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      # Build and push Docker image with Buildx (don't push on PR)
+      # https://github.com/docker/build-push-action
+      - name: Build development
+        id: build-and-push
+        uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
+        with:
+          context: .
+          push: false
+          tags: ${{ env.FOLDER_NAME }}
+          cache-from: type=gha
+          cache-to: type=gha,mode=max
+          load: true
+          file: Dockerfile.prod
+          target: development
+
+      - name: up containers
+        env:
+          GOOGLE_CLOUD_CREDENTIALS: ${{ secrets.GOOGLE_CLOUD_CREDENTIALS }}
+          GOOGLE_CLOUD_STORAGE_BUCKET_NAME: ${{ secrets.GOOGLE_CLOUD_STORAGE_BUCKET_NAME }}
+        run: docker compose -f docker-compose.ci.yaml up -d
+
+      - name: wait for mysql
+        run: docker compose -f docker-compose.ci.yaml exec -T db mysqladmin ping --silent --wait=30 -uroot -proot
+
+      - name: generate envs
+        run: |
+          docker compose -f docker-compose.ci.yaml exec -u root -T app cp ./envs/.env.test.example ./envs/.env.test
+          docker compose -f docker-compose.ci.yaml exec -u root -T app cp ./envs/.env.e2e.example ./envs/.env.e2e
+
+      - name: run unit and integration tests
+        run: docker compose -f docker-compose.ci.yaml exec -T app npm run test
+
+      - name: run e2e tests
+        run: docker compose -f docker-compose.ci.yaml exec -T app npm run test:e2e -- --runInBand --detectOpenHandles --forceExit
+
+      - name: Bump version and push tag
+        id: tagging
+        if: github.event_name != 'pull_request'
+        uses: mathieudutour/github-tag-action@v6.1
+        with:
+          github_token: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Build prod version and push
+        id: build-final
+        uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
+        with:
+          context: .
+          push: ${{ github.event_name != 'pull_request' }}
+          tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tagging.outputs.new_tag }}
+          cache-from: type=gha
+          cache-to: type=gha,mode=max
+          file: Dockerfile.prod
+
+  cd:
+    needs: ci
+    runs-on: ubuntu-latest
+
+    if: ${{ github.event_name != 'pull_request' }}
+
+    steps:
+      - name: show version
+        run: echo ${{ needs.ci.outputs.new_version }}
diff --git a/docker-compose.ci.yaml b/docker-compose.ci.yaml
index e85bd91..9dff819 100644
--- a/docker-compose.ci.yaml
+++ b/docker-compose.ci.yaml
@@ -18,6 +18,9 @@ services:
     depends_on:
       - db
       - rabbitmq
+    environment:
+      - GOOGLE_CLOUD_STORAGE_BUCKET_NAME
+      - GOOGLE_CLOUD_CREDENTIALS
 
   db:
     image: mysql:8.0.30-debian
diff --git a/src/@types/supertest.d.ts b/src/@types/supertest.d.ts
index df06aa1..c25f1c7 100644
--- a/src/@types/supertest.d.ts
+++ b/src/@types/supertest.d.ts
@@ -2,6 +2,6 @@ import superagent from 'superagent';
 
 declare module 'supertest' {
   interface Test extends superagent.SuperAgentRequest {
-    authenticate(app: INestApplication, forceAdmin: boolean = true): this;
+    authenticate(app: INestApplication, forceAdmin?: boolean): this;
   }
 }
diff --git a/src/core/shared/infra/config.ts b/src/core/shared/infra/config.ts
index f657da2..b15298c 100644
--- a/src/core/shared/infra/config.ts
+++ b/src/core/shared/infra/config.ts
@@ -37,8 +37,13 @@ export class Config {
       return;
     }
 
-    Config.env = readEnv({
+    const { parsed } = readEnv({
       path: join(__dirname, `../../../../envs/.env.${process.env.NODE_ENV}`),
-    }).parsed;
+    });
+
+    Config.env = {
+      ...parsed,
+      ...process.env,
+    };
   }
 }
diff --git a/test/categories/delete-category.e2e-spec.ts b/test/categories/delete-category.e2e-spec.ts
index c84f162..bcdd8c6 100644
--- a/test/categories/delete-category.e2e-spec.ts
+++ b/test/categories/delete-category.e2e-spec.ts
@@ -31,6 +31,7 @@ describe('CategoriesController (e2e)', () => {
       test.each(arrange)('when id is $id', async ({ id, expected }) => {
         return request(appHelper.app.getHttpServer())
           .delete(`/categories/${id}`)
+          .authenticate(appHelper.app)
           .expect(expected.statusCode)
           .expect(expected);
       });
@@ -45,6 +46,7 @@ describe('CategoriesController (e2e)', () => {
 
       await request(appHelper.app.getHttpServer())
         .delete(`/categories/${category.category_id.id}`)
+        .authenticate(appHelper.app)
         .expect(204);
 
       await expect(
diff --git a/test/categories/get-category.e2e-spec.ts b/test/categories/get-category.e2e-spec.ts
index 26a5fcc..b64af99 100644
--- a/test/categories/get-category.e2e-spec.ts
+++ b/test/categories/get-category.e2e-spec.ts
@@ -35,6 +35,7 @@ describe('CategoriesController (e2e)', () => {
       test.each(arrange)('when id is $id', async ({ id, expected }) => {
         return request(nestApp.app.getHttpServer())
           .get(`/categories/${id}`)
+          .authenticate(nestApp.app)
           .expect(expected.statusCode)
           .expect(expected);
       });
@@ -49,6 +50,7 @@ describe('CategoriesController (e2e)', () => {
 
       const res = await request(nestApp.app.getHttpServer())
         .get(`/categories/${category.category_id.id}`)
+        .authenticate(nestApp.app)
         .expect(200);
       const keyInResponse = GetCategoryFixture.keysInResponse;
       expect(Object.keys(res.body)).toStrictEqual(['data']);
diff --git a/test/categories/list-categories.e2e-spec.ts b/test/categories/list-categories.e2e-spec.ts
index be39020..7658523 100644
--- a/test/categories/list-categories.e2e-spec.ts
+++ b/test/categories/list-categories.e2e-spec.ts
@@ -28,6 +28,7 @@ describe('CategoriesController (e2e)', () => {
           const queryParams = new URLSearchParams(send_data as any).toString();
           return request(nestApp.app.getHttpServer())
             .get(`/categories/?${queryParams}`)
+            .authenticate(nestApp.app)
             .expect(200)
             .expect({
               data: expected.entities.map((e) =>
@@ -61,6 +62,7 @@ describe('CategoriesController (e2e)', () => {
           const queryParams = new URLSearchParams(send_data as any).toString();
           return request(nestApp.app.getHttpServer())
             .get(`/categories/?${queryParams}`)
+            .authenticate(nestApp.app)
             .expect(200)
             .expect({
               data: expected.entities.map((e) =>
diff --git a/test/categories/update-category.e2e-spec.ts b/test/categories/update-category.e2e-spec.ts
index 6390c9d..443c465 100644
--- a/test/categories/update-category.e2e-spec.ts
+++ b/test/categories/update-category.e2e-spec.ts
@@ -43,6 +43,7 @@ describe('CategoriesController (e2e)', () => {
         async ({ id, send_data, expected }) => {
           return request(nestApp.app.getHttpServer())
             .patch(`/categories/${id}`)
+            .authenticate(nestApp.app)
             .send(send_data)
             .expect(expected.statusCode)
             .expect(expected);
@@ -60,6 +61,7 @@ describe('CategoriesController (e2e)', () => {
       test.each(arrange)('when body is $label', ({ value }) => {
         return request(app.app.getHttpServer())
           .patch(`/categories/${uuid}`)
+          .authenticate(app.app)
           .send(value.send_data)
           .expect(422)
           .expect(value.expected);
@@ -86,6 +88,7 @@ describe('CategoriesController (e2e)', () => {
         await categoryRepo.insert(category);
         return request(app.app.getHttpServer())
           .patch(`/categories/${category.category_id.id}`)
+          .authenticate(app.app)
           .send(value.send_data)
           .expect(422)
           .expect(value.expected);
@@ -110,6 +113,7 @@ describe('CategoriesController (e2e)', () => {
 
           const res = await request(appHelper.app.getHttpServer())
             .patch(`/categories/${categoryCreated.category_id.id}`)
+            .authenticate(appHelper.app)
             .send(send_data)
             .expect(200);
           const keyInResponse = UpdateCategoryFixture.keysInResponse;