diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..19d488e21 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +.env* +tmp/ +dist/ +.git \ No newline at end of file diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 000000000..27bcee3fb --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,4 @@ +template: | + ## What’s Changed + + $CHANGES diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..bc3897888 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,130 @@ +name: Deploy apps/portal + +on: + push: + branches: + - ENG-2003-devops + + workflow_dispatch: + + pull_request_target: + types: [opened, edited, synchronize, reopened, labeled, unlabeled] + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + + steps: + - name: Check if important variables are set + shell: bash + run: | + if [[ ${{ secrets.AWS_ACCESS_KEY_ID }} == '' ]]; then + echo "secret AWS_ACCESS_KEY_ID not set" + exit 1 + fi + if [[ ${{ secrets.AWS_SECRET_ACCESS_KEY }} == '' ]]; then + echo "secret AWS_SECRET_ACCESS_KEY not set" + exit 1 + fi + if [[ ${{ secrets.AWS_ACCOUNT }} == '' ]]; then + echo "secret AWS_ACCOUNT not set" + exit 1 + fi + + - name: Set variables + shell: bash + run: | + IS_DISPATCH=${{ github.event_name == 'workflow_dispatch' }} + + if [[ $IS_DISPATCH = 'true' ]]; then + MY_ENV=production + else + MY_ENV=dev + fi + + ECR_IMAGE=${{ secrets.AWS_ACCOUNT }}.dkr.ecr.${{ secrets.AWS_REGION }}.amazonaws.com/portal-$MY_ENV:latest + CLUSTER_NAME=systems-intuition-$MY_ENV-cluster + SERVICE_NAME=portal-intuition-$MY_ENV + + echo "MY_ENV=$MY_ENV" >> $GITHUB_ENV + echo "CLUSTER_NAME=$CLUSTER_NAME" >> $GITHUB_ENV + echo "SERVICE_NAME=$SERVICE_NAME" >> $GITHUB_ENV + echo "ECR_IMAGE=$ECR_IMAGE" >> $GITHUB_ENV + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v1 + with: + mask-password: true + + - name: Build, tag, and push image to AWS ECR + run: | + docker build \ + --platform linux/x86_64 \ + -t ${{ env.ECR_IMAGE }} \ + -f apps/portal/Dockerfile \ + . \ + --build-arg ALCHEMY_MAINNET_API_KEY=${{ secrets.ALCHEMY_MAINNET_API_KEY }} \ + --build-arg ALCHEMY_API_KEY=${{ secrets.ALCHEMY_API_KEY }} \ + --build-arg ALCHEMY_MAINNET_RPC_URL=${{ secrets.ALCHEMY_MAINNET_RPC_URL }} \ + --build-arg ALCHEMY_BASE_SEPOLIA_RPC_URL=${{ secrets.ALCHEMY_BASE_SEPOLIA_RPC_URL }} \ + --build-arg ALCHEMY_BASE_RPC_URL=${{ secrets.ALCHEMY_BASE_RPC_URL }} \ + --build-arg WALLETCONNECT_PROJECT_ID=${{ secrets.WALLETCONNECT_PROJECT_ID }} \ + --build-arg SESSION_SECRET=${{ secrets.SESSION_SECRET }} \ + --build-arg API_URL=${{ secrets.API_URL }} \ + --build-arg API_KEY=${{ secrets.API_KEY }} \ + --build-arg PRIVY_APP_ID=${{ secrets.PRIVY_APP_ID }} \ + --build-arg PRIVY_APP_SECRET=${{ secrets.PRIVY_APP_SECRET }} \ + --build-arg CLOUDINARY_CLOUD_NAME=${{ secrets.CLOUDINARY_CLOUD_NAME }} \ + --build-arg CLOUDINARY_API_KEY=${{ secrets.CLOUDINARY_API_KEY }} \ + --build-arg CLOUDINARY_API_SECRET=${{ secrets.CLOUDINARY_API_SECRET }} + docker push ${{ env.ECR_IMAGE }} + + - name: Install AWS CLI + uses: unfor19/install-aws-cli-action@v1 + with: + version: 1 + + - name: Download task definition + shell: bash + run: | + aws ecs describe-task-definition --task-definition portal-intuition-${{ env.MY_ENV }} --query taskDefinition > task-definition.json + + - name: Render Amazon ECS task definition + id: render-container + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: task-definition.json + container-name: portal + image: ${{ env.ECR_IMAGE }} + environment-variables: | + AWS_REGION=${{ secrets.AWS_REGION }} + AWS_ACCOUNT=${{ secrets.AWS_ACCOUNT }} + + - name: Deploy task definition to Amazon ECS + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: ${{ steps.render-container.outputs.task-definition }} + service: ${{ env.SERVICE_NAME }} + cluster: ${{ env.CLUSTER_NAME }} + wait-for-service-stability: true + + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 2d3cf4355..d9fde43e3 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ tmp/ packages/1ui/storybook-static .env* -!.env.example \ No newline at end of file +!.env.example +.secrets diff --git a/apps/portal/Dockerfile b/apps/portal/Dockerfile new file mode 100644 index 000000000..ad3d49077 --- /dev/null +++ b/apps/portal/Dockerfile @@ -0,0 +1,66 @@ +FROM docker.io/node:lts-alpine as base + +ARG ALCHEMY_MAINNET_API_KEY=${ALCHEMY_MAINNET_API_KEY} +ARG ALCHEMY_API_KEY=${ALCHEMY_API_KEY} +ARG ALCHEMY_MAINNET_RPC_URL=${ALCHEMY_MAINNET_RPC_URL} +ARG ALCHEMY_BASE_SEPOLIA_RPC_URL=${ALCHEMY_BASE_SEPOLIA_RPC_URL} +ARG ALCHEMY_BASE_RPC_URL=${ALCHEMY_BASE_RPC_URL} +ARG WALLETCONNECT_PROJECT_ID=${WALLETCONNECT_PROJECT_ID} +ARG SESSION_SECRET=${SESSION_SECRET} +ARG API_URL=${API_URL} +ARG API_KEY=${API_KEY} +ARG PRIVY_APP_ID=${PRIVY_APP_ID} +ARG PRIVY_APP_SECRET=${PRIVY_APP_SECRET} +ARG CLOUDINARY_CLOUD_NAME=${CLOUDINARY_CLOUD_NAME} +ARG CLOUDINARY_API_KEY=${CLOUDINARY_API_KEY} +ARG CLOUDINARY_API_SECRET=${CLOUDINARY_API_SECRET} + +ENV ALCHEMY_MAINNET_API_KEY=${ALCHEMY_MAINNET_API_KEY} +ENV ALCHEMY_API_KEY=${ALCHEMY_API_KEY} +ENV ALCHEMY_MAINNET_RPC_URL=${ALCHEMY_MAINNET_RPC_URL} +ENV ALCHEMY_BASE_SEPOLIA_RPC_URL=${ALCHEMY_BASE_SEPOLIA_RPC_URL} +ENV ALCHEMY_BASE_RPC_URL=${ALCHEMY_BASE_RPC_URL} +ENV WALLETCONNECT_PROJECT_ID=${WALLETCONNECT_PROJECT_ID} +ENV SESSION_SECRET=${SESSION_SECRET} +ENV API_URL=${API_URL} +ENV API_KEY=${API_KEY} +ENV PRIVY_APP_ID=${PRIVY_APP_ID} +ENV PRIVY_APP_SECRET=${PRIVY_APP_SECRET} +ENV CLOUDINARY_CLOUD_NAME=${CLOUDINARY_CLOUD_NAME} +ENV CLOUDINARY_API_KEY=${CLOUDINARY_API_KEY} +ENV CLOUDINARY_API_SECRET=${CLOUDINARY_API_SECRET} + +WORKDIR /app + +COPY package.json \ + project.json \ + tsconfig* \ + nx.json \ + pnpm*.yaml \ + .eslintrc.base.cjs \ + .verdaccio \ + ./ +COPY apps/portal ./apps/portal +COPY packages ./packages + +RUN apk add --no-cache \ + python3 \ + make \ + gcc \ + g++ + +RUN npm install -g pnpm@9.0.6 + +FROM base as build +RUN npm add --global nx@latest +RUN pnpm i +RUN pnpm run portal:build + +FROM base +COPY --from=build /app /app +ENV NX_REJECT_UNKNOWN_LOCAL_CACHE=0 +ENV PORT=8080 +ENV HOST=0.0.0.0 + +EXPOSE 8080 +CMD [ "pnpm", "run", "portal:start" ] diff --git a/apps/portal/package.json b/apps/portal/package.json index 4a5ee49aa..f92484503 100644 --- a/apps/portal/package.json +++ b/apps/portal/package.json @@ -7,7 +7,7 @@ "scripts": { "build": "remix vite:build --emptyOutDir", "dev": "remix vite:dev", - "start": "HOST=localhost remix-serve build/server/index.js", + "start": "HOST=0.0.0.0 PORT=8080 NODE_ENV=production node build/server/index.js", "lint:fix": "pnpm lint --fix" }, "repository": { diff --git a/apps/portal/vite.config.ts b/apps/portal/vite.config.ts index 68e99a6b5..a3d3286ed 100644 --- a/apps/portal/vite.config.ts +++ b/apps/portal/vite.config.ts @@ -40,7 +40,7 @@ export default defineConfig({ tsconfigPaths(), ], server: { - port: 3000, + port: 8080, }, build: { target: 'ES2022',