diff --git a/.cursorignore b/.cursorignore index 79d16e57..6bcc42d9 100644 --- a/.cursorignore +++ b/.cursorignore @@ -1,5 +1,3 @@ -!.github/workflows/ - # Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) # Ignore all files in the node_modules directory @@ -61,7 +59,8 @@ deployments/ ocf/build/ # GitHub workflows -.github/ +!.github/workflows/ +!.github/workflows/*.yml # Generated files *.gen.ts diff --git a/.github/workflows/deploy.dev.yaml b/.github/workflows/deploy.dev.yaml index e492cffa..c104198e 100644 --- a/.github/workflows/deploy.dev.yaml +++ b/.github/workflows/deploy.dev.yaml @@ -33,7 +33,7 @@ jobs: ${{ runner.os }}-yarn- - name: Install Node Dependencies - run: yarn install --frozen-lockfile --network-concurrency 5 --no-progress --verbose + run: yarn install --frozen-lockfile --network-concurrency 5 --no-progress - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 @@ -52,6 +52,9 @@ jobs: - name: Check Formatting run: yarn format:check + - name: Build Application + run: npx esbuild src/app.js --bundle --platform=node --outfile=dist/app.cjs + - name: Deploy shell: bash env: @@ -91,7 +94,7 @@ jobs: 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 . && \ + docker build -t ocp-dev:${DEPLOY_TIME} -f Dockerfile . && \ echo 'Cleaning up old resources...' && \ docker ps -q --filter 'publish=8081' | xargs -r docker rm -f && \ @@ -106,7 +109,6 @@ jobs: --health-interval='2s' \ --health-retries='3' \ --health-timeout='5s' \ - --restart always \ -e DOCKER_ENV='true' \ -e NODE_ENV='development' \ -e SENTRY_DSN='${SENTRY_DSN}' \ @@ -118,7 +120,7 @@ jobs: -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ ocp-dev:${DEPLOY_TIME} && \ - wait_for_health \"\$CONTAINER_NAME\" && \ + wait_for_health \"\$CONTAINER_NAME\" if [ \$? -eq 0 ]; then handle_container_switch \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"dev\" else diff --git a/.github/workflows/deploy.prod.yaml b/.github/workflows/deploy.prod.yaml index 022c084c..e1bb59ce 100644 --- a/.github/workflows/deploy.prod.yaml +++ b/.github/workflows/deploy.prod.yaml @@ -33,7 +33,7 @@ jobs: ${{ runner.os }}-yarn- - name: Install Node Dependencies - run: yarn install --frozen-lockfile --network-concurrency 5 --no-progress --verbose + run: yarn install --frozen-lockfile --network-concurrency 5 --no-progress - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 @@ -52,6 +52,9 @@ jobs: - name: Check Formatting run: yarn format:check + - name: Build Application + run: npx esbuild src/app.js --bundle --platform=node --outfile=dist/app.cjs + - name: Deploy shell: bash env: @@ -91,7 +94,7 @@ jobs: 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 . && \ + docker build -t ocp-prod:${DEPLOY_TIME} -f Dockerfile . && \ echo 'Cleaning up old resources...' && \ docker ps -q --filter 'publish=8081' | xargs -r docker rm -f && \ @@ -106,7 +109,6 @@ jobs: --health-interval='2s' \ --health-retries='3' \ --health-timeout='5s' \ - --restart always \ -e DOCKER_ENV='true' \ -e NODE_ENV='production' \ -e SENTRY_DSN='${SENTRY_DSN}' \ @@ -118,7 +120,7 @@ jobs: -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ ocp-prod:${DEPLOY_TIME} && \ - wait_for_health \"\$CONTAINER_NAME\" && \ + wait_for_health \"\$CONTAINER_NAME\" if [ \$? -eq 0 ]; then handle_container_switch \"\$CONTAINER_NAME\" \"${DEPLOY_TIME}\" \"prod\" else diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..7b7f1f03 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Use the official Node.js 18 image +FROM node:18 + +# Set working directory +WORKDIR /app + +# Environment variables to avoid platform-specific esbuild bloat +ENV npm_config_platform=linux +ENV npm_config_arch=x64 +ENV NODE_ENV=production + +# Copy dependency definitions first to leverage Docker cache +COPY package.json yarn.lock ./ + +# Install dependencies +# Using --frozen-lockfile is best practice for CI/CD +RUN yarn install --frozen-lockfile --network-concurrency 5 --no-progress + +# Copy app source and pre-built files +COPY . . + +# Expose app port +EXPOSE 8080 + +# Run the pre-built application +CMD ["node", "dist/app.cjs"] diff --git a/Dockerfile.dev b/Dockerfile.dev deleted file mode 100644 index c2d6384f..00000000 --- a/Dockerfile.dev +++ /dev/null @@ -1,15 +0,0 @@ -# Use the official node image as a parent image -FROM node:18 - -# Set the working directory -WORKDIR /app - -# Copy package files first to leverage Docker cache -COPY package.json yarn.lock ./ -RUN yarn install - -# Then copy the rest of the files -COPY . . - -EXPOSE 8080 -CMD ["yarn", "dev"] diff --git a/Dockerfile.prod b/Dockerfile.prod deleted file mode 100644 index a86de9aa..00000000 --- a/Dockerfile.prod +++ /dev/null @@ -1,31 +0,0 @@ -# Use the official Node.js 18 image -FROM node:18 - -# Set working directory -WORKDIR /app - -# 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 ./ - -# Install ping for connectivity check (optional for debugging only) -RUN apt-get update && apt-get install -y iputils-ping - -# Run basic connectivity test (can be removed later) -RUN ping -c 2 registry.yarnpkg.com - -# Install dependencies with caching and better error handling -RUN yarn install --frozen-lockfile --network-concurrency 5 --no-progress --verbose - -# Copy application source -COPY . . - -# Expose your app port -EXPOSE 8080 - -# Default command -CMD ["yarn", "start"] diff --git a/package.json b/package.json index 349f3c38..1a92ad4c 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.19.0", "chalk": "4", + "esbuild": "^0.23.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-node": "^0.3.9", diff --git a/scripts/docker_container_utils.sh b/scripts/docker_container_utils.sh index 404794d3..cfcccc67 100644 --- a/scripts/docker_container_utils.sh +++ b/scripts/docker_container_utils.sh @@ -14,18 +14,18 @@ wait_for_health() { local timeout_duration=30 local start_time=$(date +%s) local end_time=$((start_time + timeout_duration)) - + echo "Waiting for container to be healthy..." while (( $(date +%s) < end_time )); do local status=$(check_health "$container_name") echo "Container health status: [$status]" - + if [[ "$status" = "healthy" ]]; then return 0 fi sleep 2 done - + return 1 } @@ -39,7 +39,7 @@ handle_container_switch() { # Get current container config local config=$(docker inspect "$container_name" --format='{{range .Config.Env}} -e {{.}}{{end}}') local image=$(docker inspect "$container_name" --format='{{.Config.Image}}') - + # Create new container but don't start it yet echo "Creating final container..." docker create \ @@ -53,7 +53,7 @@ handle_container_switch() { $config \ -v '/home/ubuntu/global-bundle.pem:/global-bundle.pem' \ $image - + # Atomic switch: stop old container and start new one as quickly as possible echo "Performing atomic switch..." ( @@ -63,21 +63,21 @@ handle_container_switch() { docker rename ocp-${environment}-final ocp-${environment} docker start ocp-${environment} ) & # Run in background - + # Wait for background process wait $! - + # Verify new container is running if ! docker ps --filter "name=ocp-${environment}" --filter "status=running" | grep -q ocp-${environment}; then echo "Switch failed, rolling back..." return 1 fi - + # Stop and remove the old container docker stop "$container_name" docker rm "$container_name" - + echo 'Performing final cleanup...' # Force remove old images docker image ls "ocp-${environment}:*" --format '{{.ID}}' | tail -n +3 | xargs -r docker image rm -f @@ -85,7 +85,7 @@ handle_container_switch() { docker system prune -af --volumes # Tag the current image as latest docker tag "ocp-${environment}:${deploy_time}" ocp-${environment}:latest - + echo 'Deployment successful!' cd /home/ubuntu && rm -rf "app-${deploy_time}" return 0 @@ -97,9 +97,11 @@ handle_failed_deployment() { local deploy_time="$2" local environment="$3" echo 'New container failed health check, rolling back...' + echo "Dumping logs for container: $container_name" + docker logs "$container_name" docker stop "$container_name" docker rm "$container_name" docker image rm "ocp-${environment}:${deploy_time}" cd /home/ubuntu && rm -rf "app-${deploy_time}" return 1 -} \ No newline at end of file +} diff --git a/src/app.js b/src/app.js index efda438f..91831dd0 100644 --- a/src/app.js +++ b/src/app.js @@ -103,6 +103,11 @@ app.use("/manifest", manifestRoutes); // transactions app.use("/transactions/", contractMiddleware, transactionRoutes); +// Health check endpoint +app.get("/health", (req, res) => { + res.status(200).send("OK"); +}); + const startServer = async () => { // Connect to MongoDB await connectDB(); diff --git a/yarn.lock b/yarn.lock index 4ce458d0..18ff4fc9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3322,7 +3322,7 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -esbuild@~0.23.0: +esbuild@^0.23.0, esbuild@~0.23.0: version "0.23.1" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.1.tgz#40fdc3f9265ec0beae6f59824ade1bd3d3d2dab8" integrity sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==