Skip to content

Commit c91caa5

Browse files
author
claudfuen
committed
fix: use AWS ECR public Node.js image for reliable builds
- Switch to public.ecr.aws/lambda/nodejs:18-x86_64 (official AWS image) - Use npm ci for dependency installation (no rate limits) - Use npx tsx for TypeScript execution (standard tooling) - Resolves Docker Hub rate limits and missing Bun image issues
1 parent f8ed239 commit c91caa5

File tree

4 files changed

+332
-9
lines changed

4 files changed

+332
-9
lines changed

β€Ž.github/workflows/deploy.ymlβ€Ž

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
name: Deploy Pathfinder
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch: # Allow manual triggers
7+
8+
env:
9+
AWS_REGION: us-east-1
10+
11+
jobs:
12+
deploy:
13+
name: Deploy Infrastructure and Application
14+
runs-on: ubuntu-latest
15+
16+
permissions:
17+
id-token: write # Required for OIDC
18+
contents: read
19+
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Configure AWS credentials
25+
uses: aws-actions/configure-aws-credentials@v4
26+
with:
27+
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
28+
aws-region: ${{ env.AWS_REGION }}
29+
30+
- name: Setup Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: '20'
34+
cache: 'npm'
35+
cache-dependency-path: 'apps/infra/package-lock.json'
36+
37+
- name: Install Pulumi CLI
38+
uses: pulumi/actions@v4
39+
40+
- name: Install infrastructure dependencies
41+
run: |
42+
cd apps/infra
43+
npm ci
44+
45+
- name: Update infrastructure
46+
run: |
47+
cd apps/infra
48+
pulumi up --yes
49+
env:
50+
PULUMI_ACCESS_TOKEN: ${{ secrets.PULUMI_ACCESS_TOKEN }}
51+
NODE_ENV: development # Set for buildspec
52+
53+
- name: Wait for infrastructure stabilization
54+
run: sleep 30
55+
56+
- name: Build migration image
57+
run: |
58+
BUILD_ID=$(aws codebuild start-build \
59+
--project-name pathfinder-migration-build \
60+
--query 'build.id' --output text)
61+
echo "Migration build started: $BUILD_ID"
62+
63+
# Wait for migration build to complete
64+
aws codebuild batch-get-builds --ids $BUILD_ID \
65+
--query 'builds[0].buildStatus' --output text
66+
67+
while [ "$(aws codebuild batch-get-builds --ids $BUILD_ID --query 'builds[0].buildStatus' --output text)" = "IN_PROGRESS" ]; do
68+
echo "Waiting for migration build to complete..."
69+
sleep 30
70+
done
71+
72+
STATUS=$(aws codebuild batch-get-builds --ids $BUILD_ID --query 'builds[0].buildStatus' --output text)
73+
if [ "$STATUS" != "SUCCEEDED" ]; then
74+
echo "Migration build failed with status: $STATUS"
75+
exit 1
76+
fi
77+
echo "Migration build completed successfully"
78+
79+
- name: Run database migrations
80+
run: |
81+
TASK_ARN=$(aws ecs run-task \
82+
--cluster pathfinder \
83+
--task-definition pathfinder-migration:latest \
84+
--subnets $(aws ec2 describe-subnets --filters "Name=tag:Type,Values=private" --query 'Subnets[0].SubnetId' --output text) \
85+
--security-groups $(aws ec2 describe-security-groups --filters "Name=tag:Name,Values=pathfinder-codebuild-sg" --query 'SecurityGroups[0].GroupId' --output text) \
86+
--query 'tasks[0].taskArn' --output text)
87+
88+
echo "Migration task started: $TASK_ARN"
89+
90+
# Wait for migration task to complete
91+
aws ecs wait tasks-stopped --cluster pathfinder --tasks $TASK_ARN
92+
93+
# Check if migration succeeded
94+
EXIT_CODE=$(aws ecs describe-tasks --cluster pathfinder --tasks $TASK_ARN \
95+
--query 'tasks[0].containers[0].exitCode' --output text)
96+
97+
if [ "$EXIT_CODE" != "0" ]; then
98+
echo "Migration failed with exit code: $EXIT_CODE"
99+
exit 1
100+
fi
101+
echo "Migrations completed successfully"
102+
103+
- name: Build application image
104+
run: |
105+
BUILD_ID=$(aws codebuild start-build \
106+
--project-name pathfinder-app-build \
107+
--query 'build.id' --output text)
108+
echo "App build started: $BUILD_ID"
109+
110+
# Wait for app build to complete
111+
while [ "$(aws codebuild batch-get-builds --ids $BUILD_ID --query 'builds[0].buildStatus' --output text)" = "IN_PROGRESS" ]; do
112+
echo "Waiting for app build to complete..."
113+
sleep 30
114+
done
115+
116+
STATUS=$(aws codebuild batch-get-builds --ids $BUILD_ID --query 'builds[0].buildStatus' --output text)
117+
if [ "$STATUS" != "SUCCEEDED" ]; then
118+
echo "App build failed with status: $STATUS"
119+
exit 1
120+
fi
121+
echo "App build completed successfully"
122+
123+
- name: Deploy application
124+
run: |
125+
# Update ECS service to use new image
126+
aws ecs update-service \
127+
--cluster pathfinder \
128+
--service pathfinder-app \
129+
--force-new-deployment
130+
131+
echo "Application deployment started"
132+
133+
# Wait for deployment to stabilize
134+
aws ecs wait services-stable \
135+
--cluster pathfinder \
136+
--services pathfinder-app
137+
138+
echo "Application deployed successfully"
139+
140+
- name: Verify deployment
141+
run: |
142+
# Get ALB DNS name
143+
ALB_DNS=$(aws elbv2 describe-load-balancers \
144+
--names pathfinder-lb \
145+
--query 'LoadBalancers[0].DNSName' --output text)
146+
147+
# Health check
148+
curl -f "http://$ALB_DNS/health" || {
149+
echo "Health check failed"
150+
exit 1
151+
}
152+
153+
echo "Deployment verification successful"
154+
echo "Application URL: http://$ALB_DNS"

β€Žapps/infra/types.tsβ€Ž

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,34 @@ export interface GithubOidcOutputs {
155155
readOnlyRoleArn: pulumi.Output<string>;
156156
}
157157

158-
export interface CodeBuildOutputs {
159-
migrationProjectName: pulumi.Output<string>;
160-
migrationProjectArn: pulumi.Output<string>;
158+
export interface BuildSystemOutputs {
161159
appProjectName: pulumi.Output<string>;
162160
appProjectArn: pulumi.Output<string>;
161+
migrationProjectName: pulumi.Output<string>;
162+
migrationProjectArn: pulumi.Output<string>;
163163
codebuildRoleArn: pulumi.Output<string>;
164164
buildInstanceType: string;
165165
buildTimeout: number;
166+
createApplicationDeployment: (
167+
app: ApplicationConfig,
168+
database: DatabaseOutputs,
169+
container: ContainerOutputs
170+
) => ApplicationDeployment;
171+
}
172+
173+
174+
175+
export interface ApplicationDeployment {
176+
appName: string;
177+
contextPath: string;
178+
buildCommands: {
179+
deployWithMigrations: string;
180+
};
181+
containerImage: pulumi.Output<string>;
182+
healthCheckPath: string;
183+
resourceRequirements: { cpu: number; memory: number };
184+
scaling: { minInstances: number; maxInstances: number; targetCpuPercent: number };
185+
buildProject: pulumi.Output<string>;
166186
}
167187

168188
export interface TailscaleOutputs {

β€Žapps/web/Dockerfile.migrationβ€Ž

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# Lightweight migration-only Docker image
2-
FROM public.ecr.aws/w0g4q7i6/bun:1.2.18-alpine
2+
FROM public.ecr.aws/lambda/nodejs:18-x86_64
33

44
WORKDIR /app
55

66
# Copy package files first for better caching
7-
COPY package.json bun.lock* ./
7+
COPY package.json ./
88

9-
# Install all dependencies (including dev deps like tsx if needed)
10-
RUN bun install --frozen-lockfile
9+
# Install dependencies using npm (reliable and available everywhere)
10+
RUN npm ci --production=false
1111

1212
# Copy the entire src directory to avoid import path issues
1313
COPY src/ ./src/
@@ -25,5 +25,5 @@ RUN echo "=== Directory structure ===" && \
2525
# Set environment for better error reporting
2626
ENV NODE_ENV=production
2727

28-
# Default command runs migrations with verbose output
29-
CMD ["sh", "-c", "echo 'Starting migration container...' && bun run scripts/run-migrations.ts"]
28+
# Default command runs migrations with verbose output using npx tsx
29+
CMD ["sh", "-c", "echo 'Starting migration container...' && npx tsx scripts/run-migrations.ts"]

β€Žscripts/deploy.shβ€Ž

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# Colors for output
5+
RED='\033[0;31m'
6+
GREEN='\033[0;32m'
7+
YELLOW='\033[1;33m'
8+
NC='\033[0m' # No Color
9+
10+
# Configuration
11+
AWS_REGION=${AWS_REGION:-us-east-1}
12+
CLUSTER_NAME="pathfinder"
13+
MIGRATION_PROJECT="pathfinder-migration-build"
14+
APP_PROJECT="pathfinder-app-build"
15+
16+
echo -e "${GREEN}πŸš€ Starting Pathfinder deployment...${NC}"
17+
18+
# Function to wait for CodeBuild
19+
wait_for_build() {
20+
local build_id=$1
21+
local project_name=$2
22+
23+
echo -e "${YELLOW}⏳ Waiting for $project_name build to complete...${NC}"
24+
25+
while true; do
26+
status=$(aws codebuild batch-get-builds --ids "$build_id" --query 'builds[0].buildStatus' --output text)
27+
28+
case $status in
29+
"SUCCEEDED")
30+
echo -e "${GREEN}βœ… $project_name build completed successfully${NC}"
31+
return 0
32+
;;
33+
"FAILED"|"FAULT"|"STOPPED"|"TIMED_OUT")
34+
echo -e "${RED}❌ $project_name build failed with status: $status${NC}"
35+
return 1
36+
;;
37+
"IN_PROGRESS")
38+
echo " Still building..."
39+
sleep 30
40+
;;
41+
*)
42+
echo " Status: $status"
43+
sleep 30
44+
;;
45+
esac
46+
done
47+
}
48+
49+
# Step 1: Update Infrastructure
50+
echo -e "${YELLOW}πŸ“‹ Step 1: Updating infrastructure...${NC}"
51+
cd apps/infra
52+
export NODE_ENV=development # Required for buildspec
53+
pulumi up --yes
54+
cd ../..
55+
echo -e "${GREEN}βœ… Infrastructure updated${NC}"
56+
57+
# Wait for infrastructure to stabilize
58+
echo -e "${YELLOW}⏳ Waiting for infrastructure to stabilize...${NC}"
59+
sleep 30
60+
61+
# Step 2: Build Migration Image
62+
echo -e "${YELLOW}πŸ”¨ Step 2: Building migration image...${NC}"
63+
migration_build_id=$(aws codebuild start-build \
64+
--project-name "$MIGRATION_PROJECT" \
65+
--query 'build.id' --output text)
66+
67+
echo "Migration build ID: $migration_build_id"
68+
wait_for_build "$migration_build_id" "Migration"
69+
70+
# Step 3: Run Database Migrations
71+
echo -e "${YELLOW}πŸ—ƒοΈ Step 3: Running database migrations...${NC}"
72+
73+
# Get subnet and security group for migration task
74+
private_subnet=$(aws ec2 describe-subnets \
75+
--filters "Name=tag:Type,Values=private" \
76+
--query 'Subnets[0].SubnetId' --output text)
77+
78+
codebuild_sg=$(aws ec2 describe-security-groups \
79+
--filters "Name=tag:Name,Values=pathfinder-codebuild-sg" \
80+
--query 'SecurityGroups[0].GroupId' --output text)
81+
82+
# Run migration task
83+
migration_task_arn=$(aws ecs run-task \
84+
--cluster "$CLUSTER_NAME" \
85+
--task-definition pathfinder-migration:latest \
86+
--subnets "$private_subnet" \
87+
--security-groups "$codebuild_sg" \
88+
--query 'tasks[0].taskArn' --output text)
89+
90+
echo "Migration task ARN: $migration_task_arn"
91+
92+
# Wait for migration to complete
93+
echo -e "${YELLOW}⏳ Waiting for migrations to complete...${NC}"
94+
aws ecs wait tasks-stopped --cluster "$CLUSTER_NAME" --tasks "$migration_task_arn"
95+
96+
# Check migration exit code
97+
exit_code=$(aws ecs describe-tasks \
98+
--cluster "$CLUSTER_NAME" \
99+
--tasks "$migration_task_arn" \
100+
--query 'tasks[0].containers[0].exitCode' --output text)
101+
102+
if [ "$exit_code" != "0" ]; then
103+
echo -e "${RED}❌ Migration failed with exit code: $exit_code${NC}"
104+
exit 1
105+
fi
106+
echo -e "${GREEN}βœ… Migrations completed successfully${NC}"
107+
108+
# Step 4: Build Application Image
109+
echo -e "${YELLOW}πŸ”¨ Step 4: Building application image...${NC}"
110+
app_build_id=$(aws codebuild start-build \
111+
--project-name "$APP_PROJECT" \
112+
--query 'build.id' --output text)
113+
114+
echo "App build ID: $app_build_id"
115+
wait_for_build "$app_build_id" "Application"
116+
117+
# Step 5: Deploy Application
118+
echo -e "${YELLOW}🚒 Step 5: Deploying application...${NC}"
119+
120+
# Update ECS service
121+
aws ecs update-service \
122+
--cluster "$CLUSTER_NAME" \
123+
--service pathfinder-app \
124+
--force-new-deployment > /dev/null
125+
126+
echo -e "${YELLOW}⏳ Waiting for deployment to stabilize...${NC}"
127+
aws ecs wait services-stable \
128+
--cluster "$CLUSTER_NAME" \
129+
--services pathfinder-app
130+
131+
echo -e "${GREEN}βœ… Application deployed successfully${NC}"
132+
133+
# Step 6: Verify Deployment
134+
echo -e "${YELLOW}πŸ” Step 6: Verifying deployment...${NC}"
135+
136+
# Get ALB DNS name
137+
alb_dns=$(aws elbv2 describe-load-balancers \
138+
--names pathfinder-lb \
139+
--query 'LoadBalancers[0].DNSName' --output text)
140+
141+
# Health check
142+
if curl -sf "http://$alb_dns/health" > /dev/null; then
143+
echo -e "${GREEN}βœ… Health check passed${NC}"
144+
echo -e "${GREEN}πŸŽ‰ Deployment completed successfully!${NC}"
145+
echo -e "${GREEN}🌐 Application URL: http://$alb_dns${NC}"
146+
else
147+
echo -e "${RED}❌ Health check failed${NC}"
148+
exit 1
149+
fi

0 commit comments

Comments
Β (0)