-
Notifications
You must be signed in to change notification settings - Fork 0
add eks deply configs and test for server and client #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
341ea49
add eks deply configs and test for server and client
codic-yeeshu 47eee75
Merge branch 'main' into infra/eks
codic-yeeshu d89044f
update code rabbit fixes
codic-yeeshu f5374d0
fix lock file
codic-yeeshu de91c9c
fix package json pkgs
codic-yeeshu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,300 @@ | ||
| name: Deploy to EKS | ||
|
|
||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
| workflow_dispatch: | ||
|
|
||
| concurrency: | ||
| group: deploy-eks-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| env: | ||
| AWS_REGION: ${{ vars.AWS_REGION || 'us-east-1' }} | ||
| EKS_CLUSTER_NAME: ${{ vars.EKS_CLUSTER_NAME || 'stathub-eks' }} | ||
| K8S_NAMESPACE: stathub | ||
| ECR_SERVER_REPO: stathub-server | ||
| ECR_CLIENT_REPO: stathub-client | ||
|
|
||
| jobs: | ||
| # ---------------------------------------------------------------------- | ||
| # Phase 1 - Run unit, integration and e2e tests for client and server. | ||
| # ---------------------------------------------------------------------- | ||
| test: | ||
| name: Phase 1 - Tests | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repo | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Install pnpm | ||
| uses: pnpm/action-setup@v4 | ||
|
|
||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: 24.14.0 | ||
| cache: pnpm | ||
|
|
||
| - name: Install server dependencies | ||
| working-directory: server | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Run server tests with coverage | ||
| working-directory: server | ||
| run: pnpm run test:ci | ||
|
|
||
| - name: Install client dependencies | ||
| working-directory: client | ||
| run: pnpm install --frozen-lockfile | ||
|
|
||
| - name: Run client tests with coverage | ||
| working-directory: client | ||
| run: pnpm run test:ci | ||
|
|
||
| - name: Upload server test report | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: server-test-report | ||
| path: | | ||
| server/junit.xml | ||
| server/coverage/ | ||
| if-no-files-found: ignore | ||
|
|
||
| - name: Upload client test report | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: client-test-report | ||
| path: | | ||
| client/junit.xml | ||
| client/coverage/ | ||
| if-no-files-found: ignore | ||
|
|
||
| # ---------------------------------------------------------------------- | ||
| # Phase 2 - Provision the S3 artifact bucket and ECR repos with Terraform. | ||
| # Skipped on pull requests so PRs don't mutate cloud infra. | ||
| # ---------------------------------------------------------------------- | ||
| terraform: | ||
| name: Phase 2 - Terraform Apply | ||
| needs: test | ||
| if: github.event_name != 'pull_request' | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| ecr_server_url: ${{ steps.tf_outputs.outputs.ecr_server_url }} | ||
| ecr_client_url: ${{ steps.tf_outputs.outputs.ecr_client_url }} | ||
| bucket_name: ${{ steps.tf_outputs.outputs.bucket_name }} | ||
| defaults: | ||
| run: | ||
| working-directory: infra/terraform | ||
| steps: | ||
| - name: Checkout repo | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Configure AWS credentials | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} | ||
| aws-region: ${{ env.AWS_REGION }} | ||
|
|
||
| - name: Setup Terraform | ||
| uses: hashicorp/setup-terraform@v3 | ||
| with: | ||
| terraform_version: 1.10.5 | ||
|
|
||
| # Remote backend uses an S3 bucket bootstrapped out of band (see | ||
| # DEPLOY_EKS.md "State backend bootstrap"). The bucket name is held | ||
| # in the TF_STATE_BUCKET repo variable so it can be rotated without | ||
| # editing Terraform source. | ||
| - name: Terraform Init | ||
| env: | ||
| TF_STATE_BUCKET: ${{ vars.TF_STATE_BUCKET }} | ||
| run: | | ||
| if [ -z "${TF_STATE_BUCKET}" ]; then | ||
| echo "::error::Repo variable TF_STATE_BUCKET is not set. See DEPLOY_EKS.md." | ||
| exit 1 | ||
| fi | ||
| terraform init -input=false \ | ||
| -backend-config="bucket=${TF_STATE_BUCKET}" \ | ||
| -backend-config="key=stathub/${{ env.AWS_REGION }}/terraform.tfstate" \ | ||
| -backend-config="region=${{ env.AWS_REGION }}" | ||
|
|
||
| - name: Terraform Validate | ||
| run: terraform validate | ||
|
|
||
| - name: Terraform Plan | ||
| run: terraform plan -input=false -out=tfplan | ||
|
|
||
| - name: Terraform Apply | ||
| run: terraform apply -input=false -auto-approve tfplan | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| - name: Capture outputs | ||
| id: tf_outputs | ||
| run: | | ||
| echo "ecr_server_url=$(terraform output -raw ecr_server_repository_url)" >> "$GITHUB_OUTPUT" | ||
| echo "ecr_client_url=$(terraform output -raw ecr_client_repository_url)" >> "$GITHUB_OUTPUT" | ||
| echo "bucket_name=$(terraform output -raw artifacts_bucket_name)" >> "$GITHUB_OUTPUT" | ||
|
|
||
| # ---------------------------------------------------------------------- | ||
| # Phase 3 - Build server and client images and push them to ECR. | ||
| # ---------------------------------------------------------------------- | ||
| build_and_push: | ||
| name: Phase 3 - Build & Push Images | ||
| needs: terraform | ||
| if: github.event_name != 'pull_request' | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| server_image: ${{ steps.tags.outputs.server_image }} | ||
| client_image: ${{ steps.tags.outputs.client_image }} | ||
| steps: | ||
| - name: Checkout repo | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Configure AWS credentials | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} | ||
| aws-region: ${{ env.AWS_REGION }} | ||
|
|
||
| - name: Login to Amazon ECR | ||
| id: ecr | ||
| uses: aws-actions/amazon-ecr-login@v2 | ||
|
|
||
| - name: Set up Docker Buildx | ||
| uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Compute image tags | ||
| id: tags | ||
| run: | | ||
| SHA="${GITHUB_SHA::7}" | ||
| echo "server_image=${{ needs.terraform.outputs.ecr_server_url }}:${SHA}" >> "$GITHUB_OUTPUT" | ||
| echo "client_image=${{ needs.terraform.outputs.ecr_client_url }}:${SHA}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Build and push server image | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: ./server | ||
| push: true | ||
| tags: | | ||
| ${{ steps.tags.outputs.server_image }} | ||
| ${{ needs.terraform.outputs.ecr_server_url }}:latest | ||
| cache-from: type=gha,scope=server | ||
| cache-to: type=gha,mode=max,scope=server | ||
|
|
||
| - name: Build and push client image | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: ./client | ||
| push: true | ||
| tags: | | ||
| ${{ steps.tags.outputs.client_image }} | ||
| ${{ needs.terraform.outputs.ecr_client_url }}:latest | ||
| build-args: | | ||
| VITE_API_BASE_URL=${{ vars.VITE_API_BASE_URL }} | ||
| VITE_GOOGLE_CLIENT_ID=${{ secrets.VITE_GOOGLE_CLIENT_ID }} | ||
| cache-from: type=gha,scope=client | ||
| cache-to: type=gha,mode=max,scope=client | ||
|
|
||
| # ---------------------------------------------------------------------- | ||
| # Phase 4 - Deploy to EKS. Renders manifests with the freshly built image | ||
| # tags and applies them. The cluster itself is provisioned out of band | ||
| # (see DEPLOY_EKS.md for the eksctl steps). | ||
| # ---------------------------------------------------------------------- | ||
| deploy_eks: | ||
| name: Phase 4 - Deploy to EKS | ||
| needs: build_and_push | ||
| if: github.event_name != 'pull_request' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repo | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Configure AWS credentials | ||
| uses: aws-actions/configure-aws-credentials@v4 | ||
| with: | ||
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | ||
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | ||
| aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} | ||
| aws-region: ${{ env.AWS_REGION }} | ||
|
|
||
| - name: Install kubectl | ||
| uses: azure/setup-kubectl@v4 | ||
| with: | ||
| version: v1.30.0 | ||
|
|
||
| - name: Update kubeconfig for EKS cluster | ||
| run: | | ||
| aws eks update-kubeconfig \ | ||
| --region "${AWS_REGION}" \ | ||
| --name "${EKS_CLUSTER_NAME}" | ||
|
|
||
| - name: Verify cluster connectivity | ||
| run: | | ||
| kubectl version --client | ||
| kubectl get nodes | ||
|
|
||
| - name: Apply namespace | ||
| run: kubectl apply -f infra/k8s/namespace.yaml | ||
|
|
||
| - name: Create or update server Secret | ||
| env: | ||
| DATABASE_URL: ${{ secrets.SERVER_ENV_DATABASE_URL }} | ||
| JWT_SECRET: ${{ secrets.SERVER_ENV_JWT_SECRET }} | ||
| ARCJET_KEY: ${{ secrets.SERVER_ENV_ARCJET_KEY }} | ||
| ARCJET_MODE: ${{ secrets.SERVER_ENV_ARCJET_MODE }} | ||
| RESEND_API_KEY: ${{ secrets.SERVER_ENV_RESEND_API_KEY }} | ||
| GOOGLE_CLIENT_ID: ${{ secrets.SERVER_ENV_GOOGLE_CLIENT_ID }} | ||
| GOOGLE_CLIENT_SECRET: ${{ secrets.SERVER_ENV_GOOGLE_CLIENT_SECRET }} | ||
| CLIENT_URL: ${{ vars.SERVER_ENV_CLIENT_URL }} | ||
| CLIENT_PASSWORD_RESET_URL: ${{ vars.SERVER_ENV_CLIENT_URL }}${{ vars.SERVER_ENV_CLIENT_PASSWORD_RESET_ENDPOINT }} | ||
| run: | | ||
| kubectl -n "${K8S_NAMESPACE}" create secret generic stathub-server-secrets \ | ||
| --from-literal=DATABASE_URL="${DATABASE_URL}" \ | ||
| --from-literal=JWT_SECRET="${JWT_SECRET}" \ | ||
| --from-literal=ARCJET_KEY="${ARCJET_KEY}" \ | ||
| --from-literal=ARCJET_MODE="${ARCJET_MODE}" \ | ||
| --from-literal=RESEND_API_KEY="${RESEND_API_KEY}" \ | ||
| --from-literal=GOOGLE_CLIENT_ID="${GOOGLE_CLIENT_ID}" \ | ||
| --from-literal=GOOGLE_CLIENT_SECRET="${GOOGLE_CLIENT_SECRET}" \ | ||
| --from-literal=CLIENT_URL="${CLIENT_URL}" \ | ||
| --from-literal=CLIENT_PASSWORD_RESET_URL="${CLIENT_PASSWORD_RESET_URL}" \ | ||
| --dry-run=client -o yaml | kubectl apply -f - | ||
|
|
||
| - name: Render and apply server manifests | ||
| env: | ||
| SERVER_IMAGE: ${{ needs.build_and_push.outputs.server_image }} | ||
| run: | | ||
| sed "s|SERVER_IMAGE_PLACEHOLDER|${SERVER_IMAGE}|g" infra/k8s/server-deployment.yaml | kubectl apply -f - | ||
| kubectl apply -f infra/k8s/server-service.yaml | ||
| kubectl apply -f infra/k8s/server-service-lb.yaml | ||
|
|
||
| - name: Render and apply client manifests | ||
| env: | ||
| CLIENT_IMAGE: ${{ needs.build_and_push.outputs.client_image }} | ||
| run: | | ||
| sed "s|CLIENT_IMAGE_PLACEHOLDER|${CLIENT_IMAGE}|g" infra/k8s/client-deployment.yaml | kubectl apply -f - | ||
| kubectl apply -f infra/k8s/client-service.yaml | ||
|
|
||
| - name: Wait for server rollout | ||
| run: kubectl -n "${K8S_NAMESPACE}" rollout status deployment/stathub-server --timeout=5m | ||
|
|
||
| - name: Wait for client rollout | ||
| run: kubectl -n "${K8S_NAMESPACE}" rollout status deployment/stathub-client --timeout=5m | ||
|
|
||
| - name: Print service endpoints | ||
| run: | | ||
| echo "Pods:" | ||
| kubectl -n "${K8S_NAMESPACE}" get pods -o wide | ||
| echo "" | ||
| echo "Services:" | ||
| kubectl -n "${K8S_NAMESPACE}" get svc | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,34 @@ | ||
| # Stage 1: Build Step | ||
| FROM node:24.14.0-alpine AS build-step | ||
|
|
||
| # Set work directory | ||
| WORKDIR /usr/src/app | ||
|
|
||
| # Copy package files and install dependencies | ||
| COPY package.json ./ | ||
| COPY pnpm-lock.yaml ./ | ||
| COPY package.json pnpm-lock.yaml ./ | ||
|
|
||
| # Install dependencies efficiently | ||
| RUN corepack enable | ||
| RUN corepack enable && pnpm install --frozen-lockfile | ||
|
|
||
| RUN pnpm install --frozen-lockfile | ||
|
|
||
| # Copy the rest of the application | ||
| COPY . . | ||
|
|
||
| # Set environment variables | ||
| ARG VITE_API_BASE_URL | ||
| ARG VITE_GOOGLE_CLIENT_ID | ||
| ENV VITE_API_BASE_URL=$VITE_API_BASE_URL | ||
| ENV VITE_GOOGLE_CLIENT_ID=$VITE_GOOGLE_CLIENT_ID | ||
|
|
||
| # Build the application | ||
| RUN pnpm run build | ||
|
|
||
| # Stage 2: Nginx Server | ||
| FROM nginx:1.28.2-alpine | ||
| # nginx-unprivileged runs as the non-root "nginx" user (uid 101) by default | ||
| # and listens on 8080 instead of 80, which avoids needing CAP_NET_BIND_SERVICE. | ||
| FROM nginxinc/nginx-unprivileged:1.28-alpine | ||
|
|
||
| USER root | ||
| RUN apk add --no-cache wget | ||
| USER nginx | ||
|
|
||
| COPY --chown=nginx:nginx nginx.conf /etc/nginx/nginx.conf | ||
| COPY --from=build-step --chown=nginx:nginx /usr/src/app/dist /usr/share/nginx/html | ||
|
|
||
| # Copy the Nginx config file | ||
| COPY nginx.conf /etc/nginx/nginx.conf | ||
| EXPOSE 8080 | ||
|
|
||
| # Copy built application from build-step | ||
| COPY --from=build-step /usr/src/app/dist /usr/share/nginx/html | ||
| HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ | ||
| CMD wget --quiet --spider http://127.0.0.1:8080/healthz || exit 1 | ||
|
|
||
| # Start Nginx | ||
| CMD ["nginx", "-g", "daemon off;"] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.