diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..4ffeb0b1 --- /dev/null +++ b/.env.example @@ -0,0 +1,19 @@ +AUTH0_BASE_URL='http://dev.local:3000' +AUTH0_CLIENT_ID='Your client ID' +AUTH0_CLIENT_SECRET='Your client secret' +AUTH0_ISSUER_BASE_URL='https://shape-docs-dev.eu.auth0.com' +AUTH0_MANAGEMENT_CLIENT_ID='Your client ID' +AUTH0_MANAGEMENT_CLIENT_SECRET='Your client secret' +AUTH0_MANAGEMENT_DOMAIN='shape-docs-dev.eu.auth0.com' +AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value' +GITHUB_APP_ID=123456 +GITHUB_CLIENT_ID='GitHub App client ID' +GITHUB_CLIENT_SECRET='GitHub App client secret' +GITHUB_ORGANIZATION_NAME='shapehq' +GITHUB_PRIVATE_KEY_BASE_64='base 64 encoded version of the private key' +GITHUB_WEBHOK_REPOSITORY_ALLOWLIST='' +GITHUB_WEBHOK_REPOSITORY_DISALLOWLIST='' +GITHUB_WEBHOOK_SECRET='preshared secret also put in app conf in GitHub' +NEXT_PUBLIC_SHAPE_DOCS_TITLE='Shape Docs' +REDIS_URL='' +SHAPE_DOCS_BASE_URL='https://docs.shapetools.io' \ No newline at end of file diff --git a/.github/workflows/build-and-deploy-staging.yml b/.github/workflows/build-and-deploy-staging.yml new file mode 100644 index 00000000..8b46715d --- /dev/null +++ b/.github/workflows/build-and-deploy-staging.yml @@ -0,0 +1,82 @@ +name: "[Staging] Build and Deploy" + +on: + workflow_dispatch: {} + +env: + AWS_REGION: eu-central-1 + ECR_REPOSITORY: shapedocs + ECS_SERVICE: StagingApp-AppService7F8F0CA1-jCcia0OvXEXa + ECS_CLUSTER: StagingApp-EcsDefaultClusterMnL3mNNYNVPC9C1EC7A3-L9xrshUBnmqe + ECS_TASK_DEFINITION_NAME: StagingAppAppServiceTaskDef1613562E + CONTAINER_NAME: web + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_SHAPE_DOCS }} + +jobs: + build: + name: Build + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install 1Password CLI + uses: 1password/install-cli-action@v1 + + - name: Install AWS credentials from 1Password + run: | + AWS_ACCESS_KEY_ID=$(op read "op://Shape Docs GitHub Actions/AWS GitHub Actions User/access_key_id") + AWS_SECRET_ACCESS_KEY=$(op read "op://Shape Docs GitHub Actions/AWS GitHub Actions User/secret_access_key") + echo "AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID" >> $GITHUB_ENV + echo "AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY" >> $GITHUB_ENV + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4.0.1 + with: + aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v2.0.1 + + - name: Fetch task definition + id: fetch-task-definition + run: | + aws ecs describe-task-definition --task-definition ${{ env.ECS_TASK_DEFINITION_NAME }} --region ${{ env.AWS_REGION }} | jq .taskDefinition > task-definition.json + jq . task-definition.json + + - name: Create .env.local + run: | + cp .env.example .env.local + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: ${{ github.sha }} + run: | + # Build a docker image and push it to ECR so that it can + # be deployed to ECS + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1.2.0 + with: + task-definition: task-definition.json + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.build-image.outputs.image }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1.4.11 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml deleted file mode 100644 index dca98fda..00000000 --- a/.github/workflows/build-docker-image.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: "Build Docker image" - -on: - workflow_dispatch: {} - -jobs: - build: - name: Build - runs-on: ubuntu-22.04 - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Build the app and Docker image - run: | - docker build -t shapedocs . diff --git a/Dockerfile b/Dockerfile index e55824e7..3b14c7ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-alpine AS base +FROM node:20.8.1-alpine AS base # Install dependencies only when needed FROM base AS deps diff --git a/next.config.js b/next.config.js index 5373e6a7..761f61ab 100644 --- a/next.config.js +++ b/next.config.js @@ -4,7 +4,9 @@ const nextConfig = { // Allows production builds to successfully complete even if it has linting errors. // This is only OK because we do linting as part of our CI setup. ignoreDuringBuilds: true, - } + }, + // Output standalone to be used for Docker builds. + output: 'standalone', } module.exports = nextConfig diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts new file mode 100644 index 00000000..fead38a1 --- /dev/null +++ b/src/app/api/health/route.ts @@ -0,0 +1,5 @@ +import { NextResponse } from "next/server" + +export const GET = async (): Promise => { + return NextResponse.json({ status: "Healthy" }) +} diff --git a/src/middleware.ts b/src/middleware.ts index ab6c8d87..1bd4ede1 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,7 +1,7 @@ import { withMiddlewareAuthRequired } from "@auth0/nextjs-auth0/edge" export const config = { - matcher: "/((?!api/hooks|api/auth/logout|api/auth/forceLogout|_next/static|_next/image|images|favicon.ico).*)" + matcher: "/((?!api/hooks|api/auth/logout|api/auth/forceLogout|api/health|_next/static|_next/image|images|favicon.ico).*)" } export default withMiddlewareAuthRequired()