1+ name : Deploy to AWS
2+
3+ on :
4+ workflow_call :
5+ inputs :
6+ ec2_ip :
7+ required : true
8+ type : string
9+ workflow_dispatch :
10+ inputs :
11+ ec2_ip :
12+ description : ' EC2 public IP to deploy to'
13+ required : true
14+ type : string
15+
16+ permissions :
17+ contents : read
18+ packages : read
19+
20+ jobs :
21+ deploy :
22+ runs-on : ubuntu-latest
23+ environment :
24+ name : AWS
25+ url : ' https://client.${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}.nip.io'
26+
27+ steps :
28+ - name : Checkout Code
29+ uses : actions/checkout@v4
30+
31+ - name : Test SSH, disk space & Docker registry
32+ uses : appleboy/ssh-action@v1.0.3
33+ with :
34+ host : ${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}
35+ username : ${{ vars.AWS_EC2_USER }}
36+ key : ${{ secrets.AWS_EC2_PRIVATE_KEY }}
37+ script : |
38+ set -e
39+ echo "Testing disk space:"
40+ df -h /
41+ echo "Testing Docker:"
42+ docker ps
43+ echo "Testing Docker registry login:"
44+ echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
45+ echo "All initial checks passed."
46+
47+ - name : Copy Docker Compose File From Repo to the EC2 Instance
48+ uses : appleboy/scp-action@v0.1.7
49+ with :
50+ host : ${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}
51+ username : ${{ vars.AWS_EC2_USER }}
52+ key : ${{ secrets.AWS_EC2_PRIVATE_KEY }}
53+ source : " ./docker-compose.prod.yml"
54+ target : /home/${{ vars.AWS_EC2_USER }}
55+
56+ - name : Get latest tag (or use 1.0-alpha as fallback)
57+ id : get_tag
58+ run : |
59+ TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "1.0-alpha")
60+ echo "tag=$TAG" >> $GITHUB_OUTPUT
61+ echo "Latest tag is: $TAG"
62+
63+ - name : Prepare Environment File (.env.prod)
64+ uses : appleboy/ssh-action@v1.0.3
65+ with :
66+ host : ${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}
67+ username : ${{ vars.AWS_EC2_USER }}
68+ key : ${{ secrets.AWS_EC2_PRIVATE_KEY }}
69+ script : |
70+ set -e
71+ cd /home/${{ vars.AWS_EC2_USER }}
72+ rm -f .env.prod
73+ echo "Creating .env.prod file..."
74+ echo "MONGODB_CONTAINER_NAME=skill-forge-mongo-db" >> .env.prod
75+ echo "CLIENT_CONTAINER_NAME=skill-forge-client" >> .env.prod
76+ echo "SERVER_CONTAINER_NAME=skill-forge-server" >> .env.prod
77+ echo "GENAI_CONTAINER_NAME=skill-forge-genai" >> .env.prod
78+ echo "WEAVIATE_CONTAINER_NAME=skill-forge-weaviate" >> .env.prod
79+
80+ echo "MONGODB_EXPOSED_PORT=27018" >> .env.prod
81+ echo "MONGODB_DATABASE=${{ vars.MONGODB_DATABASE }}" >> .env.prod
82+ echo "MONGODB_USERNAME=${{ secrets.MONGODB_USERNAME }}" >> .env.prod
83+ echo "MONGODB_PASSWORD=${{ secrets.MONGODB_PASSWORD }}" >> .env.prod
84+
85+ echo "SPRING_PROFILE_ACTIVES=docker" >> .env.prod
86+ echo "SERVER_PORT_GATEWAY=8081" >> .env.prod
87+ echo "SERVER_PORT_USER=8082" >> .env.prod
88+ echo "SERVER_PORT_COURSES=8083" >> .env.prod
89+
90+ echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> .env.prod
91+ echo "JWT_EXPIRATION_MS=${{ vars.JWT_EXPIRATION_MS }}" >> .env.prod
92+
93+ echo "VITE_PORT=3000" >> .env.prod
94+ echo "VITE_APP_NAME=SkillForge.ai" >> .env.prod
95+ echo "VITE_APP_VERSION=${{ steps.get_tag.outputs.tag }}" >> .env.prod
96+ echo "VITE_API_BASE_URL=/api/" >> .env.prod
97+ echo "VITE_API_VERSION=v1" >> .env.prod
98+ echo "BUILD_MODE=production" >> .env.prod
99+
100+ echo "WEAVIATE_HOST=weaviate-db" >> .env.prod
101+ echo "WEAVIATE_EXPOSED_HTTP_PORT=1234" >> .env.prod
102+ echo "WEAVIATE_EXPOSED_GRPC_PORT=50051" >> .env.prod
103+
104+ echo "GENAI_APP_NAME=skill-forge-genai" >> .env.prod
105+ echo "GENAI_APP_VERSION=${{ steps.get_tag.outputs.tag }}" >> .env.prod
106+ echo "GENAI_PORT=8888" >> .env.prod
107+ echo "CORS_ALLOW_ORIGINS=*" >> .env.prod
108+ echo "IS_DEV_MODE=0" >> .env.prod
109+ echo "UVICORN_WORKERS=${{ vars.UVICORN_WORKERS }}" >> .env.prod
110+ echo "LLM_PROVIDER=${{ vars.LLM_PROVIDER }}" >> .env.prod
111+ echo "OPENAI_API_BASE=${{ vars.OPENAI_API_BASE }}" >> .env.prod
112+ echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> .env.prod
113+ echo "OPENAI_MODEL=${{ vars.OPENAI_MODEL }}" >> .env.prod
114+
115+ echo "CLIENT_HOST=client.${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}.nip.io" >> .env.prod
116+ echo "SERVER_HOST=api.${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}.nip.io" >> .env.prod
117+ echo "PUBLIC_API_URL=https://api.${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}.nip.io/api" >> .env.prod
118+
119+ chmod 600 .env.prod
120+ echo ".env.prod file created."
121+
122+ - name : SSH to the EC2 Instance and Start Docker Compose
123+ uses : appleboy/ssh-action@v1.0.3
124+ timeout-minutes : 10
125+ with :
126+ host : ${{ github.event.inputs.ec2_ip || inputs.ec2_ip }}
127+ username : ${{ vars.AWS_EC2_USER }}
128+ key : ${{ secrets.AWS_EC2_PRIVATE_KEY }}
129+ script : |
130+ set -e
131+ cd /home/${{ vars.AWS_EC2_USER }}
132+ echo " >>> Logging into Docker registry..."
133+ echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
134+ echo " >>> Starting Docker Compose..."
135+ if ! docker compose -f docker-compose.prod.yml --env-file .env.prod up --pull=always -d; then
136+ echo "❌ Docker Compose failed. Showing service status:"
137+ docker compose -f docker-compose.prod.yml ps
138+ echo "-----------------------------------------"
139+ echo "------------ Recent logs ----------------"
140+ docker compose -f docker-compose.prod.yml logs --tail=40
141+ exit 1
142+ fi
0 commit comments