Skip to content

Commit 4e08f20

Browse files
committed
feat: implement S3 static asset hosting for persistent chunk availability
- Configure Next.js to serve static assets from S3 using assetPrefix - Add upload script to push static assets to S3 during Docker build - Update Dockerfile to include S3 upload step with proper cache control - Update GitHub workflow to pass S3 credentials and bucket info - Enable zero-downtime deployments by keeping old chunks accessible - Use git commit SHA for asset versioning and cache invalidation
1 parent 6ed2ca7 commit 4e08f20

File tree

4 files changed

+93
-9
lines changed

4 files changed

+93
-9
lines changed

.github/workflows/build-and-push.yml

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,20 @@ jobs:
194194
cache-from: type=registry,ref=${{ matrix.private && format('{0}/{1}', env.ECR_REGISTRY, matrix.repo_name) || format('{0}/{1}/{2}', matrix.registry, matrix.owner, matrix.repo_name) }}:latest
195195
cache-to: type=inline
196196
secrets: ${{ matrix.private && format('SENTRY_AUTH_TOKEN={0}', secrets.SENTRY_AUTH_TOKEN) || '' }}
197-
build-args: |
198-
NEXT_PUBLIC_DOCS_URL=${{ matrix.private && vars.NEXT_PUBLIC_DOCS_URL || '' }}
199-
NEXT_PUBLIC_LATITUDE_CLOUD_PAYMENT_URL=${{ matrix.private && vars.NEXT_PUBLIC_LATITUDE_CLOUD_PAYMENT_URL || '' }}
200-
NEXT_PUBLIC_POSTHOG_HOST=${{ matrix.private && secrets.NEXT_PUBLIC_POSTHOG_HOST || '' }}
201-
NEXT_PUBLIC_POSTHOG_KEY=${{ matrix.private && secrets.NEXT_PUBLIC_POSTHOG_KEY || '' }}
202-
NEXT_PUBLIC_SENTRY_WEB_DSN=${{ matrix.private && vars.SENTRY_WEB_DSN || '' }}
203-
SENTRY_ORG=${{ matrix.private && secrets.SENTRY_ORG || '' }}
204-
SENTRY_PROJECT=${{ matrix.private && secrets.SENTRY_PROJECT || '' }}
205-
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY=${{ secrets.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY }}
197+
build-args: |
198+
NEXT_PUBLIC_DOCS_URL=${{ matrix.private && vars.NEXT_PUBLIC_DOCS_URL || '' }}
199+
NEXT_PUBLIC_LATITUDE_CLOUD_PAYMENT_URL=${{ matrix.private && vars.NEXT_PUBLIC_LATITUDE_CLOUD_PAYMENT_URL || '' }}
200+
NEXT_PUBLIC_POSTHOG_HOST=${{ matrix.private && secrets.NEXT_PUBLIC_POSTHOG_HOST || '' }}
201+
NEXT_PUBLIC_POSTHOG_KEY=${{ matrix.private && secrets.NEXT_PUBLIC_POSTHOG_KEY || '' }}
202+
NEXT_PUBLIC_SENTRY_WEB_DSN=${{ matrix.private && vars.SENTRY_WEB_DSN || '' }}
203+
SENTRY_ORG=${{ matrix.private && secrets.SENTRY_ORG || '' }}
204+
SENTRY_PROJECT=${{ matrix.private && secrets.SENTRY_PROJECT || '' }}
205+
NEXT_SERVER_ACTIONS_ENCRYPTION_KEY=${{ secrets.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY }}
206+
AWS_ACCESS_KEY_ID=${{ matrix.private && secrets.AWS_ACCESS_KEY_ID || '' }}
207+
AWS_SECRET_ACCESS_KEY=${{ matrix.private && secrets.AWS_SECRET_ACCESS_KEY || '' }}
208+
AWS_REGION=${{ matrix.private && vars.AWS_REGION || '' }}
209+
S3_BUCKET=${{ matrix.private && vars.STATIC_ASSETS_S3_BUCKET || '' }}
210+
BUILD_ID=${{ github.sha }}
206211
207212
run-migrations:
208213
needs: [build-and-push]

apps/web/docker/Dockerfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,27 @@ RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
108108
NODE_OPTIONS="--max-old-space-size=8192" \
109109
pnpm turbo build --filter="${PROJECT}..."
110110

111+
# Upload static assets to S3 for persistent availability
112+
ARG AWS_ACCESS_KEY_ID
113+
ARG AWS_SECRET_ACCESS_KEY
114+
ARG AWS_REGION
115+
ARG S3_BUCKET
116+
ARG BUILD_ID
117+
ENV AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
118+
ENV AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
119+
ENV AWS_REGION=$AWS_REGION
120+
ENV S3_BUCKET=$S3_BUCKET
121+
ENV BUILD_ID=$BUILD_ID
122+
123+
RUN if [ -n "$S3_BUCKET" ] && [ -n "$BUILD_ID" ]; then \
124+
echo "Uploading static assets to S3..."; \
125+
chmod +x ./apps/web/scripts/upload-static-assets.sh && \
126+
cd apps/web && \
127+
./scripts/upload-static-assets.sh; \
128+
else \
129+
echo "Skipping S3 upload - S3_BUCKET or BUILD_ID not provided"; \
130+
fi
131+
111132
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm prune --prod --no-optional
112133

113134
# PRODUCTION

apps/web/next.config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const nextConfig = {
3838
images: {
3939
remotePatterns: [new URL('https://assets.pipedream.net/**')],
4040
},
41+
// Serve static assets from S3 for persistent chunk availability
42+
assetPrefix: process.env.NEXT_PUBLIC_STATIC_ASSETS_URL,
4143
}
4244

4345
let config
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/bin/bash
2+
3+
# Script to upload Next.js static assets to S3
4+
# This ensures persistent availability of chunks across deployments
5+
6+
set -e
7+
8+
# Environment variables required:
9+
# AWS_ACCESS_KEY_ID - AWS access key
10+
# AWS_SECRET_ACCESS_KEY - AWS secret key
11+
# AWS_REGION - AWS region
12+
# S3_BUCKET - S3 bucket name for static assets
13+
# BUILD_ID - Unique build identifier (e.g., git commit SHA)
14+
15+
if [ -z "$AWS_ACCESS_KEY_ID" ] || [ -z "$AWS_SECRET_ACCESS_KEY" ] || [ -z "$AWS_REGION" ] || [ -z "$S3_BUCKET" ] || [ -z "$BUILD_ID" ]; then
16+
echo "Error: Missing required environment variables"
17+
echo "Required: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, S3_BUCKET, BUILD_ID"
18+
exit 1
19+
fi
20+
21+
STATIC_DIR="./.next/static"
22+
S3_PREFIX="static-assets/$BUILD_ID"
23+
24+
echo "Uploading static assets to S3..."
25+
echo "Bucket: $S3_BUCKET"
26+
echo "Prefix: $S3_PREFIX"
27+
echo "Source: $STATIC_DIR"
28+
29+
if [ ! -d "$STATIC_DIR" ]; then
30+
echo "Error: Static directory $STATIC_DIR does not exist"
31+
exit 1
32+
fi
33+
34+
# Upload static assets with appropriate cache control headers
35+
# JS/CSS chunks: cache for 1 year (immutable)
36+
# Other assets: cache for 1 hour
37+
aws s3 cp "$STATIC_DIR" "s3://$S3_BUCKET/$S3_PREFIX/" \
38+
--recursive \
39+
--cache-control "public, max-age=31536000, immutable" \
40+
--exclude "*" \
41+
--include "*.js" \
42+
--include "*.css"
43+
44+
# Upload other static assets (images, fonts, etc.) with shorter cache
45+
aws s3 cp "$STATIC_DIR" "s3://$S3_BUCKET/$S3_PREFIX/" \
46+
--recursive \
47+
--cache-control "public, max-age=3600" \
48+
--exclude "*.js" \
49+
--exclude "*.css"
50+
51+
echo "Static assets uploaded successfully to s3://$S3_BUCKET/$S3_PREFIX/"
52+
53+
# Output the S3 URL for use in Next.js configuration
54+
S3_URL="https://$S3_BUCKET.s3.$AWS_REGION.amazonaws.com/$S3_PREFIX"
55+
echo "Static assets URL: $S3_URL"
56+
echo "NEXT_PUBLIC_STATIC_ASSETS_URL=$S3_URL" >> $GITHUB_ENV

0 commit comments

Comments
 (0)