From d499a33e45858804117c493b52ebd5029f9585d2 Mon Sep 17 00:00:00 2001 From: Thibauld Favre Date: Wed, 18 Jun 2025 12:55:03 -0400 Subject: [PATCH 1/3] Improved Dockerfile + Workflow file --- .cursorignore | 1 + .github/workflows/deploy.dev.yaml | 202 +++++++++++------------------- Dockerfile.prod | 32 ++--- 3 files changed, 91 insertions(+), 144 deletions(-) create mode 100644 .cursorignore diff --git a/.cursorignore b/.cursorignore new file mode 100644 index 00000000..9a4dd009 --- /dev/null +++ b/.cursorignore @@ -0,0 +1 @@ +!.github/workflows/ diff --git a/.github/workflows/deploy.dev.yaml b/.github/workflows/deploy.dev.yaml index a65b8b58..998da4dc 100644 --- a/.github/workflows/deploy.dev.yaml +++ b/.github/workflows/deploy.dev.yaml @@ -1,134 +1,78 @@ name: Deployment (Dev) on: - push: - branches: - - dev + push: + branches: + - dev jobs: - build: - name: Build, Test, and Deploy - environment: dev - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - submodules: true - fetch-depth: 0 - - # Setup and cache dependencies - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: "18" - cache: "yarn" - - - name: Install Node Dependencies - run: yarn install --frozen-lockfile - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: stable - - - name: Run Forge Install Script - run: chmod +x setup.sh && ./setup.sh - - # Run all tests and checks - - name: Run Forge Tests - run: cd chain && forge test --summary - - - name: Run ESLint - run: yarn lint:check - - - name: Check Formatting - run: yarn format:check - - # Build and Deploy - - name: Deploy - shell: bash - env: - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - HOST: ${{ secrets.LIGHTSAIL_INSTANCE_PUBLIC_IP_DEV }} - PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} - ETHERSCAN_L2_API_KEY: ${{ secrets.ETHERSCAN_L2_API_KEY }} - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - RPC_URL: ${{ secrets.RPC_URL }} - - run: | - # Generate timestamp for deployment - DEPLOY_TIME=$(date +%s) - echo "DEPLOY_TIME: $DEPLOY_TIME" - - # Save SSH key and set permissions - echo "$SSH_PRIVATE_KEY" > deploy_key - chmod 600 deploy_key - - # Create a temp directory for deployment - DEPLOY_DIR="/tmp/deploy-${DEPLOY_TIME}" - mkdir -p $DEPLOY_DIR - - # Copy necessary files to temp directory - echo "Preparing deployment files..." - cp -r . $DEPLOY_DIR/ - - # Sync files to server - echo "Syncing files to server..." - rsync -az --delete \ - --exclude='node_modules' \ - --exclude='.git' \ - --exclude='deploy_key' \ - --include='chain/out' \ - --include='chain/out/**' \ - -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \ - $DEPLOY_DIR/ \ - ubuntu@"$HOST":/home/ubuntu/app-${DEPLOY_TIME} - - # Execute deployment on server - ssh -i deploy_key -o StrictHostKeyChecking=no ubuntu@"$HOST" " - sudo su && \ - cd /home/ubuntu/app-${DEPLOY_TIME} && \ - echo 'Building image on host...' && \ - # Source the functions - source ./scripts/docker_container_utils.sh && \ - docker build -t ocp-dev:${DEPLOY_TIME} -f Dockerfile.dev . && \ - - # Initial cleanup - echo 'Cleaning up old resources...' && \ - docker ps -q --filter 'publish=8081' | xargs -r docker rm -f && \ - docker ps -q --filter 'publish=8082' | xargs -r docker rm -f && \ - docker container prune -f && \ - docker image prune -f && \ - - # Start new container - echo 'Starting new container...' && \ - CONTAINER_NAME=ocp-dev-${DEPLOY_TIME} && \ - - # Run container - docker run --name \$CONTAINER_NAME -d \ - --health-cmd='curl -f http://localhost:8080/health || exit 1' \ - --health-interval='2s' \ - --health-retries='3' \ - --health-timeout='5s' \ - --restart always \ - -e DOCKER_ENV='true' \ - -e NODE_ENV='development' \ - -e SENTRY_DSN='${SENTRY_DSN}' \ - -e DATABASE_URL='${DATABASE_URL}' \ - -e RPC_URL='${RPC_URL}' \ - -e PORT=8080 \ - -e PRIVATE_KEY='${PRIVATE_KEY}' \ - -e ETHERSCAN_L2_API_KEY='${ETHERSCAN_L2_API_KEY}' \ - -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ - ocp-dev:${DEPLOY_TIME} && \ - - # Wait for container to be healthy - wait_for_health "\$CONTAINER_NAME" && \ - if [ \$? -eq 0 ]; then - handle_container_switch "\$CONTAINER_NAME" "${DEPLOY_TIME}" "dev" - else - handle_failed_deployment "\$CONTAINER_NAME" "${DEPLOY_TIME}" "dev" - fi - " + build: + name: Build, Test, and Deploy + environment: dev + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18" + cache: "yarn" + + - name: Cache Yarn files manually (extra resilience) + uses: actions/cache@v3 + with: + path: ~/.cache/yarn + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Node Dependencies + run: yarn install --frozen-lockfile --network-concurrency 5 --no-progress --verbose + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: stable + + - name: Run Forge Install Script + run: chmod +x setup.sh && ./setup.sh + + - name: Run Forge Tests + run: cd chain && forge test --summary + + - name: Run ESLint + run: yarn lint:check + + - name: Check Formatting + run: yarn format:check + + - name: Deploy + shell: bash + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + HOST: ${{ secrets.LIGHTSAIL_INSTANCE_PUBLIC_IP_DEV }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + ETHERSCAN_L2_API_KEY: ${{ secrets.ETHERSCAN_L2_API_KEY }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + RPC_URL: ${{ secrets.RPC_URL }} + run: | + DEPLOY_TIME=$(date +%s) + echo "DEPLOY_TIME: $DEPLOY_TIME" + + echo "$SSH_PRIVATE_KEY" > deploy_key + chmod 600 deploy_key + + DEPLOY_DIR="/tmp/deploy-${DEPLOY_TIME}" + mkdir -p $DEPLOY_DIR + + echo "Preparing deployment files..." + cp -r . $DEPLOY_DIR/ + + echo "Syncing files to server..." + rsync -az --dele diff --git a/Dockerfile.prod b/Dockerfile.prod index 99c06f7d..a86de9aa 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,29 +1,31 @@ -# Use the official node image as a parent image +# Use the official Node.js 18 image FROM node:18 -# Set the working directory +# Set working directory WORKDIR /app -# Copy package files first to leverage Docker cache +# Define build-time environment to limit esbuild platform downloads +ENV npm_config_platform=linux +ENV npm_config_arch=x64 +ENV ESBUILD_BINARY_PATH=node_modules/esbuild/bin/esbuild + +# Copy dependency definitions early to leverage Docker cache COPY package.json yarn.lock ./ -# Debug network connectivity -# Print Node and Yarn versions for debugging -RUN node -v && yarn -v -# List files before install for debugging -RUN ls -al /app -# Check network connectivity +# Install ping for connectivity check (optional for debugging only) RUN apt-get update && apt-get install -y iputils-ping -RUN ping -c 4 registry.yarnpkg.com -# Run yarn install with verbose logging -RUN yarn install --verbose +# Run basic connectivity test (can be removed later) +RUN ping -c 2 registry.yarnpkg.com -# List files after install for debugging -RUN ls -al /app +# Install dependencies with caching and better error handling +RUN yarn install --frozen-lockfile --network-concurrency 5 --no-progress --verbose -# Then copy the rest of the files +# Copy application source COPY . . +# Expose your app port EXPOSE 8080 + +# Default command CMD ["yarn", "start"] From aae8afb32eac09c577e260977f1f227613b85cfd Mon Sep 17 00:00:00 2001 From: Thibauld Favre Date: Wed, 18 Jun 2025 13:01:55 -0400 Subject: [PATCH 2/3] fix Workflow file --- .github/workflows/deploy.dev.yaml | 53 +++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.dev.yaml b/.github/workflows/deploy.dev.yaml index 998da4dc..e492cffa 100644 --- a/.github/workflows/deploy.dev.yaml +++ b/.github/workflows/deploy.dev.yaml @@ -10,6 +10,7 @@ jobs: name: Build, Test, and Deploy environment: dev runs-on: ubuntu-latest + steps: - name: Checkout code uses: actions/checkout@v2 @@ -23,7 +24,7 @@ jobs: node-version: "18" cache: "yarn" - - name: Cache Yarn files manually (extra resilience) + - name: Cache Yarn files (manual fallback) uses: actions/cache@v3 with: path: ~/.cache/yarn @@ -75,4 +76,52 @@ jobs: cp -r . $DEPLOY_DIR/ echo "Syncing files to server..." - rsync -az --dele + rsync -az --delete \ + --exclude='node_modules' \ + --exclude='.git' \ + --exclude='deploy_key' \ + --include='chain/out' \ + --include='chain/out/**' \ + -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \ + $DEPLOY_DIR/ \ + ubuntu@"$HOST":/home/ubuntu/app-${DEPLOY_TIME} + + ssh -i deploy_key -o StrictHostKeyChecking=no ubuntu@"$HOST" " + sudo su && \ + cd /home/ubuntu/app-${DEPLOY_TIME} && \ + echo 'Building image on host...' && \ + source ./scripts/docker_container_utils.sh && \ + docker build -t ocp-dev:${DEPLOY_TIME} -f Dockerfile.dev . && \ + + echo 'Cleaning up old resources...' && \ + docker ps -q --filter 'publish=8081' | xargs -r docker rm -f && \ + docker ps -q --filter 'publish=8082' | xargs -r docker rm -f && \ + docker container prune -f && \ + docker image prune -f && \ + + echo 'Starting new container...' && \ + CONTAINER_NAME=ocp-dev-${DEPLOY_TIME} && \ + docker run --name \$CONTAINER_NAME -d \ + --health-cmd='curl -f http://localhost:8080/health || exit 1' \ + --health-interval='2s' \ + --health-retries='3' \ + --health-timeout='5s' \ + --restart always \ + -e DOCKER_ENV='true' \ + -e NODE_ENV='development' \ + -e SENTRY_DSN='${SENTRY_DSN}' \ + -e DATABASE_URL='${DATABASE_URL}' \ + -e RPC_URL='${RPC_URL}' \ + -e PORT=8080 \ + -e PRIVATE_KEY='${PRIVATE_KEY}' \ + -e ETHERSCAN_L2_API_KEY='${ETHERSCAN_L2_API_KEY}' \ + -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ + ocp-dev:${DEPLOY_TIME} && \ + + wait_for_health \"\$CONTAINER_NAME\" && \ + if [ \$? -eq 0 ]; then + handle_container_switch \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"dev\" + else + handle_failed_deployment \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"dev\" + fi + " From 2ba7e2d08e7fc0bc6d2f5a9cf5d7ff23d6615f37 Mon Sep 17 00:00:00 2001 From: Thibauld Favre Date: Wed, 18 Jun 2025 13:20:11 -0400 Subject: [PATCH 3/3] fix Workflow file for production deployment --- .github/workflows/deploy.prod.yaml | 250 ++++++++++++++--------------- 1 file changed, 122 insertions(+), 128 deletions(-) diff --git a/.github/workflows/deploy.prod.yaml b/.github/workflows/deploy.prod.yaml index acf215c3..022c084c 100644 --- a/.github/workflows/deploy.prod.yaml +++ b/.github/workflows/deploy.prod.yaml @@ -1,133 +1,127 @@ name: Deployment (Prod) on: - push: - branches: - - main + push: + branches: + - main jobs: - build: - name: Build, Test, and Deploy - environment: prod - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v2 - with: - submodules: true - fetch-depth: 0 - - # Setup and cache dependencies - - name: Setup Node.js - uses: actions/setup-node@v3 - with: - node-version: "18" - cache: "yarn" - - - name: Install Node Dependencies - run: yarn install --frozen-lockfile - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: stable - - - name: Run Forge Install Script - run: chmod +x setup.sh && ./setup.sh - - # Run all tests and checks - - name: Run Forge Tests - run: cd chain && forge test --summary - - - name: Run ESLint - run: yarn lint:check - - - name: Check Formatting - run: yarn format:check - - - name: Deploy - shell: bash - env: - SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} - HOST: ${{ secrets.LIGHTSAIL_INSTANCE_PUBLIC_IP_PROD }} - PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} - ETHERSCAN_L2_API_KEY: ${{ secrets.ETHERSCAN_L2_API_KEY }} - SENTRY_DSN: ${{ secrets.SENTRY_DSN }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - RPC_URL: ${{ secrets.RPC_URL }} - - run: | - # Generate timestamp for deployment - DEPLOY_TIME=$(date +%s) - echo "DEPLOY_TIME: $DEPLOY_TIME" - - # Save SSH key and set permissions - echo "$SSH_PRIVATE_KEY" > deploy_key - chmod 600 deploy_key - - # Create a temp directory for deployment - DEPLOY_DIR="/tmp/deploy-${DEPLOY_TIME}" - mkdir -p $DEPLOY_DIR - - # Copy necessary files to temp directory - echo "Preparing deployment files..." - cp -r . $DEPLOY_DIR/ - - # Sync files to server - echo "Syncing files to server..." - rsync -az --delete \ - --exclude='node_modules' \ - --exclude='.git' \ - --exclude='deploy_key' \ - --include='chain/out' \ - --include='chain/out/**' \ - -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \ - $DEPLOY_DIR/ \ - ubuntu@"$HOST":/home/ubuntu/app-${DEPLOY_TIME} - - # Execute deployment on server - ssh -i deploy_key -o StrictHostKeyChecking=no ubuntu@"$HOST" " - sudo su && \ - cd /home/ubuntu/app-${DEPLOY_TIME} && \ - echo 'Building image on host...' && \ - # Source the functions - source ./scripts/docker_container_utils.sh && \ - docker build -t ocp-prod:${DEPLOY_TIME} -f Dockerfile.prod . && \ - - # Initial cleanup - echo 'Cleaning up old resources...' && \ - docker ps -q --filter 'publish=8081' | xargs -r docker rm -f && \ - docker ps -q --filter 'publish=8082' | xargs -r docker rm -f && \ - docker container prune -f && \ - docker image prune -f && \ - - # Start new container - echo 'Starting new container...' && \ - CONTAINER_NAME=ocp-prod-${DEPLOY_TIME} && \ - - # Run container - docker run --name \$CONTAINER_NAME -d \ - --health-cmd='curl -f http://localhost:8080/health || exit 1' \ - --health-interval='2s' \ - --health-retries='3' \ - --health-timeout='5s' \ - --restart always \ - -e DOCKER_ENV='true' \ - -e NODE_ENV='production' \ - -e SENTRY_DSN='${SENTRY_DSN}' \ - -e DATABASE_URL='${DATABASE_URL}' \ - -e RPC_URL='${RPC_URL}' \ - -e PORT=8080 \ - -e PRIVATE_KEY='${PRIVATE_KEY}' \ - -e ETHERSCAN_L2_API_KEY='${ETHERSCAN_L2_API_KEY}' \ - -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ - ocp-prod:${DEPLOY_TIME} && \ - - # Wait for container to be healthy - wait_for_health \"\$CONTAINER_NAME\" && \ - if [ \$? -eq 0 ]; then - handle_container_switch \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"prod\" - else - handle_failed_deployment \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"prod\" - fi - " + build: + name: Build, Test, and Deploy + environment: prod + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + submodules: true + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: "18" + cache: "yarn" + + - name: Cache Yarn files (manual fallback) + uses: actions/cache@v3 + with: + path: ~/.cache/yarn + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Install Node Dependencies + run: yarn install --frozen-lockfile --network-concurrency 5 --no-progress --verbose + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: stable + + - name: Run Forge Install Script + run: chmod +x setup.sh && ./setup.sh + + - name: Run Forge Tests + run: cd chain && forge test --summary + + - name: Run ESLint + run: yarn lint:check + + - name: Check Formatting + run: yarn format:check + + - name: Deploy + shell: bash + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + HOST: ${{ secrets.LIGHTSAIL_INSTANCE_PUBLIC_IP_PROD }} + PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} + ETHERSCAN_L2_API_KEY: ${{ secrets.ETHERSCAN_L2_API_KEY }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + RPC_URL: ${{ secrets.RPC_URL }} + run: | + DEPLOY_TIME=$(date +%s) + echo "DEPLOY_TIME: $DEPLOY_TIME" + + echo "$SSH_PRIVATE_KEY" > deploy_key + chmod 600 deploy_key + + DEPLOY_DIR="/tmp/deploy-${DEPLOY_TIME}" + mkdir -p $DEPLOY_DIR + + echo "Preparing deployment files..." + cp -r . $DEPLOY_DIR/ + + echo "Syncing files to server..." + rsync -az --delete \ + --exclude='node_modules' \ + --exclude='.git' \ + --exclude='deploy_key' \ + --include='chain/out' \ + --include='chain/out/**' \ + -e "ssh -i deploy_key -o StrictHostKeyChecking=no" \ + $DEPLOY_DIR/ \ + ubuntu@"$HOST":/home/ubuntu/app-${DEPLOY_TIME} + + ssh -i deploy_key -o StrictHostKeyChecking=no ubuntu@"$HOST" " + sudo su && \ + cd /home/ubuntu/app-${DEPLOY_TIME} && \ + echo 'Building image on host...' && \ + source ./scripts/docker_container_utils.sh && \ + docker build -t ocp-prod:${DEPLOY_TIME} -f Dockerfile.prod . && \ + + echo 'Cleaning up old resources...' && \ + docker ps -q --filter 'publish=8081' | xargs -r docker rm -f && \ + docker ps -q --filter 'publish=8082' | xargs -r docker rm -f && \ + docker container prune -f && \ + docker image prune -f && \ + + echo 'Starting new container...' && \ + CONTAINER_NAME=ocp-prod-${DEPLOY_TIME} && \ + docker run --name \$CONTAINER_NAME -d \ + --health-cmd='curl -f http://localhost:8080/health || exit 1' \ + --health-interval='2s' \ + --health-retries='3' \ + --health-timeout='5s' \ + --restart always \ + -e DOCKER_ENV='true' \ + -e NODE_ENV='production' \ + -e SENTRY_DSN='${SENTRY_DSN}' \ + -e DATABASE_URL='${DATABASE_URL}' \ + -e RPC_URL='${RPC_URL}' \ + -e PORT=8080 \ + -e PRIVATE_KEY='${PRIVATE_KEY}' \ + -e ETHERSCAN_L2_API_KEY='${ETHERSCAN_L2_API_KEY}' \ + -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ + ocp-prod:${DEPLOY_TIME} && \ + + wait_for_health \"\$CONTAINER_NAME\" && \ + if [ \$? -eq 0 ]; then + handle_container_switch \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"prod\" + else + handle_failed_deployment \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"prod\" + fi + "