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;