From cf2fedcf996110d249617717e98699333d042616 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Sat, 7 Dec 2024 00:10:21 -0500 Subject: [PATCH 1/9] update Dockerfile --- backend/Dockerfile | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 43c3e641..ed4636c5 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,15 +1,12 @@ FROM golang:1.23-alpine AS builder + WORKDIR /app -COPY . . + +COPY go.mod go.sum ./ RUN go mod download -RUN CGO_ENABLED=0 GOOS=linux go build -o app +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux go build -o app . -FROM alpine:3.20 -RUN apk --no-cache add ca-certificates -WORKDIR /root/ -COPY --from=builder /app/app . -RUN mkdir -p static/dist -EXPOSE 6969 -ENV APP_ENV="production" CMD ["./app"] From a4b0ee8d4a42ec88a0354b54228afb00729f538f Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Sun, 8 Dec 2024 14:16:55 -0500 Subject: [PATCH 2/9] only load .env in development mode --- backend/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/main.go b/backend/main.go index 9db4f4f8..ff206057 100644 --- a/backend/main.go +++ b/backend/main.go @@ -15,7 +15,7 @@ import ( func main() { zerolog.TimeFieldFormat = zerolog.TimeFormatUnix - if os.Getenv("APP_ENV") != common.PRODUCTION_ENV { + if os.Getenv("APP_ENV") == common.DEVELOPMENT_ENV { if err := godotenv.Load(); err != nil { log.Fatal().Err(err).Msg("failed to load .env") } From c0f48a97fa5a45d02c9382442af912ba106a96c9 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Sun, 8 Dec 2024 14:17:03 -0500 Subject: [PATCH 3/9] update Dockerfile --- backend/Dockerfile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index ed4636c5..8685e3f3 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,12 +1,14 @@ FROM golang:1.23-alpine AS builder - WORKDIR /app - -COPY go.mod go.sum ./ +COPY . . RUN go mod download -COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o app -RUN CGO_ENABLED=0 GOOS=linux go build -o app . +FROM alpine:3.20 +RUN apk --no-cache add ca-certificates +WORKDIR /root/ +COPY --from=builder /app/app . +RUN mkdir -p static/dist CMD ["./app"] From 4f44d01f58c9932e690946702cd2347324d42dc7 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Sun, 8 Dec 2024 14:17:13 -0500 Subject: [PATCH 4/9] update preview deploy to staging --- .github/workflows/deploy-preview-backend.yml | 56 ------ .github/workflows/deploy-staging-backend.yml | 180 +++++++++++++++++++ 2 files changed, 180 insertions(+), 56 deletions(-) delete mode 100644 .github/workflows/deploy-preview-backend.yml create mode 100644 .github/workflows/deploy-staging-backend.yml diff --git a/.github/workflows/deploy-preview-backend.yml b/.github/workflows/deploy-preview-backend.yml deleted file mode 100644 index 4f52b263..00000000 --- a/.github/workflows/deploy-preview-backend.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Deploy Preview -on: - workflow_dispatch: - workflow_run: - workflows: ["Test Backend"] - types: - - completed - branches: ['main'] - paths: - - 'backend/**' -jobs: - deploy: - runs-on: ubuntu-latest - defaults: - run: - shell: bash - working-directory: backend - steps: - - name: Deploy to Preview Server - uses: appleboy/ssh-action@v1.0.3 - env: - MICONFIG: ${{ secrets.MICONFIG_PREVIEW }} - PK: ${{ secrets.BENTO_PK_PREVIEW }} - with: - host: ${{ secrets.PREVIEW_SERVER_IP }} - username: ${{ secrets.PREVIEW_USER }} - key: ${{ secrets.PREVIEW_SERVER_SSH_KEY }} - envs: MICONFIG,PK - script: | - clean_up() { - echo "Performing cleanup..." - cd ~ - echo "Remove repository" - rm -rf repo - echo "Docker cleanup" - docker system prune -f - echo "Done: Cleanup" - } - handle_error() { - clean_up - echo "Failed to deploy preview" - exit 1 - } - trap 'handle_error' ERR - echo "Cloning repository" - cd ~ - rm -rf repo - git clone git@github.com:${{ github.repository }}.git repo - cd repo - cd backend - echo "Copy deploy preview script" - cp ../.github/scripts/deploy-preview.sh . - echo "Running deploy-preview.sh" - chmod +x deploy-preview.sh - ./deploy-preview.sh - clean_up diff --git a/.github/workflows/deploy-staging-backend.yml b/.github/workflows/deploy-staging-backend.yml new file mode 100644 index 00000000..765e09eb --- /dev/null +++ b/.github/workflows/deploy-staging-backend.yml @@ -0,0 +1,180 @@ +name: Deploy Staging +on: + workflow_dispatch: + push: + branches: ['main'] + paths: + - 'backend/**' +jobs: + deploy: + runs-on: ubuntu-latest + environment: staging + defaults: + run: + shell: bash + working-directory: backend + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Create env file + run: | + cat << EOF > .env + APP_ENV=${{ secrets.APP_ENV }} + APP_NAME=${{ secrets.APP_NAME }} + AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_REGION=${{ secrets.AWS_REGION }} + AWS_S3_BUCKET=${{ secrets.AWS_S3_BUCKET }} + AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }} + BACKEND_URL=${{ secrets.BACKEND_URL }} + DB_HOST=${{ secrets.DB_HOST }} + DB_NAME=${{ secrets.DB_NAME }} + DB_PASSWORD=${{ secrets.DB_PASSWORD }} + DB_PORT=${{ secrets.DB_PORT }} + DB_SSLMODE=${{ secrets.DB_SSLMODE }} + DB_USER=${{ secrets.DB_USER }} + JWT_SECRET=${{ secrets.JWT_SECRET }} + JWT_SECRET_VERIFY_EMAIL=${{ secrets.JWT_SECRET_VERIFY_EMAIL }} + NOREPLY_EMAIL=${{ secrets.NOREPLY_EMAIL }} + PORT=${{ secrets.PORT }} + POSTGRES_USER=${{ secrets.DB_USER }} + POSTGRES_DB=${{ secrets.DB_NAME }} + POSTGRES_PASSWORD=${{ secrets.DB_PASSWORD }} + RESEND_API_KEY=${{ secrets.RESEND_API_KEY }} + EOF + + - name: Setup VPS fingerprint + run: | + mkdir -p ~/.ssh + echo "${{ secrets.VPS_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + echo "${{ secrets.VPS_IP }} ${{ secrets.VPS_FINGERPRINT }}" >> ~/.ssh/known_hosts + + - name: Setup Rsync + uses: GuillaumeFalourd/setup-rsync@v1.2 + + - name: Setup VPS File System Tree + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.VPS_IP }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_KEY }} + script: | + mkdir -p $HOME/staging/migrations + + - name: Upload .env + run: | + rsync -avz --progress .env ${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}:~/staging/ + + - name: Upload Migrations + run: | + rsync -avz --delete --progress .sqlc/migrations/ ${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}:~/staging/migrations/ + + - name: Boot Postgres and Run Migrations + uses: appleboy/ssh-action@v1.0.3 + env: + DB_NAME: ${{ secrets.DB_NAME }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + DB_PORT: ${{ secrets.DB_PORT }} + DB_SSLMODE: ${{ secrets.DB_SSLMODE }} + DB_USER: ${{ secrets.DB_USER }} + APP_NAME: ${{ secrets.APP_NAME }} + with: + host: ${{ secrets.VPS_IP }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_KEY }} + envs: DB_NAME,DB_PASSWORD,DB_PORT,DB_SSLMODE,DB_USER,APP_NAME + script: | + cd $HOME/staging + + POSTGRES_DATA_DIR="$HOME/staging/postgres" + mkdir -p $POSTGRES_DATA_DIR + POSTGRES_CONTAINER="$APP_NAME-postgres" + + if ! docker ps --filter "name=$POSTGRES_CONTAINER" --format '{{.Names}}' | grep -q "^$POSTGRES_CONTAINER"; then + echo "PostgreSQL container not found. Starting new container..." + docker stop $POSTGRES_CONTAINER || true + docker rm $POSTGRES_CONTAINER || true + docker run -d \ + --name "$POSTGRES_CONTAINER" \ + -p "$DB_PORT:5432" \ + -v "$POSTGRES_DATA_DIR:/var/lib/postgresql/data" \ + --env POSTGRES_USER="${{ secrets.DB_USER }}" \ + --env POSTGRES_PASSWORD="${{ secrets.DB_PASSWORD }}" \ + --env POSTGRES_DB="${{ secrets.DB_NAME }}" \ + postgres:16 + + # Wait for PostgreSQL to be ready + echo "Waiting for PostgreSQL to be ready..." + RETRY_COUNT=0 + MAX_RETRIES=10 + while ! docker exec "$POSTGRES_CONTAINER" pg_isready -q; do + RETRY_COUNT=$((RETRY_COUNT + 1)) + if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then + echo "Error: PostgreSQL failed to start after $MAX_RETRIES attempts" + exit 1 + fi + echo "Waiting for PostgreSQL to be ready..." + sleep 1 + done + echo "Postgres is ready!" + else + echo "PostgreSQL container is already running" + fi + + goose fix -dir $HOME/staging/migrations + goose -dir $HOME/staging/migrations postgres \ + "postgres://$DB_USER:$DB_PASSWORD@localhost:$DB_PORT/$DB_NAME?sslmode=$DB_SSLMODE" up + + - name: Build Docker Image + run: | + docker build -t ${{ secrets.APP_NAME }}:latest . + docker save -o image.tar ${{ secrets.APP_NAME }}:latest + + - name: Upload Docker Image + run: | + rsync -avz image.tar ${{ secrets.VPS_USER }}@${{ secrets.VPS_IP }}:~/staging/ + + - name: Deploy Go App on VPS + uses: appleboy/ssh-action@v1.0.3 + env: + DB_NAME: ${{ secrets.DB_NAME }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + DB_PORT: ${{ secrets.DB_PORT }} + DB_SSLMODE: ${{ secrets.DB_SSLMODE }} + DB_USER: ${{ secrets.DB_USER }} + APP_NAME: ${{ secrets.APP_NAME }} + with: + host: ${{ secrets.VPS_IP }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_KEY }} + envs: DB_NAME,DB_PASSWORD,DB_PORT,DB_SSLMODE,DB_USER,APP_NAME + script: | + cd $HOME/staging + + echo "Stopping and removing existing container if present..." + docker stop $APP_NAME || true + docker rm $APP_NAME || true + + echo "Loading pre-built docker image..." + docker load -i image.tar + + echo "Starting new application container..." + docker run -d \ + --name $APP_NAME \ + --env-file .env \ + --network=host --add-host=host.docker.internal:host-gateway \ + $APP_NAME:latest + + echo "Done: Preview Deployment" + + - name: Post Deployment Clean Up on VPS + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.VPS_IP }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_KEY }} + script: | + rm -rf $HOME/staging/migrations + rm -f $HOME/staging/image.tar + rm -f $HOME/staging/.env From c59900ede8653e03c63a1e35e6a3cf584175cf93 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:54:05 -0500 Subject: [PATCH 5/9] use APP_ENV as part of container name --- .github/workflows/deploy-staging-backend.yml | 21 +++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy-staging-backend.yml b/.github/workflows/deploy-staging-backend.yml index 765e09eb..b8ec130d 100644 --- a/.github/workflows/deploy-staging-backend.yml +++ b/.github/workflows/deploy-staging-backend.yml @@ -79,17 +79,18 @@ jobs: DB_SSLMODE: ${{ secrets.DB_SSLMODE }} DB_USER: ${{ secrets.DB_USER }} APP_NAME: ${{ secrets.APP_NAME }} + APP_ENV: ${{ secrets.APP_ENV }} with: host: ${{ secrets.VPS_IP }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_KEY }} - envs: DB_NAME,DB_PASSWORD,DB_PORT,DB_SSLMODE,DB_USER,APP_NAME + envs: DB_NAME,DB_PASSWORD,DB_PORT,DB_SSLMODE,DB_USER,APP_NAME,APP_ENV script: | cd $HOME/staging POSTGRES_DATA_DIR="$HOME/staging/postgres" mkdir -p $POSTGRES_DATA_DIR - POSTGRES_CONTAINER="$APP_NAME-postgres" + POSTGRES_CONTAINER="$APP_NAME-$APP_ENV-postgres" if ! docker ps --filter "name=$POSTGRES_CONTAINER" --format '{{.Names}}' | grep -q "^$POSTGRES_CONTAINER"; then echo "PostgreSQL container not found. Starting new container..." @@ -128,8 +129,8 @@ jobs: - name: Build Docker Image run: | - docker build -t ${{ secrets.APP_NAME }}:latest . - docker save -o image.tar ${{ secrets.APP_NAME }}:latest + docker build -t ${{ secrets.APP_NAME }}-${{ secrets.APP_ENV }}:latest . + docker save -o image.tar ${{ secrets.APP_NAME }}-${{ secrets.APP_ENV }}:latest - name: Upload Docker Image run: | @@ -144,27 +145,29 @@ jobs: DB_SSLMODE: ${{ secrets.DB_SSLMODE }} DB_USER: ${{ secrets.DB_USER }} APP_NAME: ${{ secrets.APP_NAME }} + APP_ENV: ${{ secrets.APP_ENV }} with: host: ${{ secrets.VPS_IP }} username: ${{ secrets.VPS_USER }} key: ${{ secrets.VPS_KEY }} - envs: DB_NAME,DB_PASSWORD,DB_PORT,DB_SSLMODE,DB_USER,APP_NAME + envs: DB_NAME,DB_PASSWORD,DB_PORT,DB_SSLMODE,DB_USER,APP_NAME,APP_ENV script: | cd $HOME/staging echo "Stopping and removing existing container if present..." - docker stop $APP_NAME || true - docker rm $APP_NAME || true + CONTAINER="$APP_NAME-$APP_ENV" + docker stop $CONTAINER || true + docker rm $CONTAINER || true echo "Loading pre-built docker image..." docker load -i image.tar echo "Starting new application container..." docker run -d \ - --name $APP_NAME \ + --name $CONTAINER \ --env-file .env \ --network=host --add-host=host.docker.internal:host-gateway \ - $APP_NAME:latest + "$APP_NAME-$APP_ENV:latest" echo "Done: Preview Deployment" From 1c4b016ef8e416fd234980eae381c216ed84da29 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Sun, 8 Dec 2024 23:01:22 -0500 Subject: [PATCH 6/9] update environment name for backend --- .github/workflows/deploy-staging-backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-staging-backend.yml b/.github/workflows/deploy-staging-backend.yml index b8ec130d..e61c7392 100644 --- a/.github/workflows/deploy-staging-backend.yml +++ b/.github/workflows/deploy-staging-backend.yml @@ -8,7 +8,7 @@ on: jobs: deploy: runs-on: ubuntu-latest - environment: staging + environment: Staging defaults: run: shell: bash From 48ea1c079b8de4de8b639db8815a5cd26e3e2fc3 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Sun, 8 Dec 2024 23:52:15 -0500 Subject: [PATCH 7/9] running frontend staging deployment action --- ...ontend.yml => deploy-staging-frontend.yml} | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) rename .github/workflows/{deploy-preview-frontend.yml => deploy-staging-frontend.yml} (70%) diff --git a/.github/workflows/deploy-preview-frontend.yml b/.github/workflows/deploy-staging-frontend.yml similarity index 70% rename from .github/workflows/deploy-preview-frontend.yml rename to .github/workflows/deploy-staging-frontend.yml index 0380ebfd..025a4545 100644 --- a/.github/workflows/deploy-preview-frontend.yml +++ b/.github/workflows/deploy-staging-frontend.yml @@ -9,6 +9,7 @@ on: jobs: build-and-deploy-frontend: runs-on: ubuntu-latest + environment: Staging defaults: run: shell: bash @@ -37,15 +38,20 @@ jobs: restore-keys: | ${{ runner.os }}-pnpm-store- - name: Install dependencies - run: cd frontend && pnpm install --frozen-lockfile + run: pnpm install --frozen-lockfile + - name: Build environment + run: | + cat << EOF > .env + VITE_API_URL=${{ secrets.BACKEND_URL }}/api/v1 + EOF - name: Build - run: cd frontend && pnpm build + run: pnpm build - name: Deploy to server uses: appleboy/scp-action@master with: - host: ${{ secrets.PREVIEW_SERVER_IP }} - username: ${{ secrets.PREVIEW_USER }} - key: ${{ secrets.PREVIEW_SERVER_SSH_KEY }} - source: "frontend/dist/" - target: "${{ secrets.TARGET_DIR }}" - strip_components: 1 + host: ${{ secrets.VPS_IP }} + username: ${{ secrets.VPS_USER }} + key: ${{ secrets.VPS_KEY }} + source: "frontend/dist/*" + target: "${{ secrets.FE_DIR }}" + strip_components: 2 From 941a60aebd798cc6eb80e0adf6f6a490bc434bbd Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Mon, 9 Dec 2024 00:11:17 -0500 Subject: [PATCH 8/9] update last echo backend deployment --- .github/workflows/deploy-staging-backend.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-staging-backend.yml b/.github/workflows/deploy-staging-backend.yml index e61c7392..e6e6fa83 100644 --- a/.github/workflows/deploy-staging-backend.yml +++ b/.github/workflows/deploy-staging-backend.yml @@ -169,7 +169,7 @@ jobs: --network=host --add-host=host.docker.internal:host-gateway \ "$APP_NAME-$APP_ENV:latest" - echo "Done: Preview Deployment" + echo "Done: Staging Deployment" - name: Post Deployment Clean Up on VPS uses: appleboy/ssh-action@v1.0.3 From 4630c87de419c674c7850100fb0e26ef91e3a536 Mon Sep 17 00:00:00 2001 From: jc <46619361+juancwu@users.noreply.github.com> Date: Mon, 9 Dec 2024 00:11:43 -0500 Subject: [PATCH 9/9] remove old deployment script --- .github/scripts/deploy-preview-backend.sh | 74 ----------------------- 1 file changed, 74 deletions(-) delete mode 100644 .github/scripts/deploy-preview-backend.sh diff --git a/.github/scripts/deploy-preview-backend.sh b/.github/scripts/deploy-preview-backend.sh deleted file mode 100644 index 9b9d52a2..00000000 --- a/.github/scripts/deploy-preview-backend.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# immediately exit if any command returns a non-zero exit status. -set -e - -# -# Preview Deployment Script -# Handles environment setup, database initialization, and application deployment -# - -echo "Deploying new preview" - -# Initialize configuration files -echo "Get .env from Konbini" -echo "$MICONFIG" > .miconfig.yaml -echo "$PK" > private.pem - -# Environment setup -mi bento order - -# Export all environment variables automatically -set -a -source .env -set +a - -# PostgreSQL setup -echo "Setting up preview PostgreSQL container" -POSTGRES_DATA_DIR="$HOME/postgres" -mkdir -p $POSTGRES_DATA_DIR -POSTGRES_CONTAINER="$APP_NAME-postgres" - -# Check and start PostgreSQL container if needed -if ! docker ps | grep $POSTGRES_CONTAINER; then - echo "PostgreSQL container not found. Starting new container..." - docker run -d \ - --name $POSTGRES_CONTAINER \ - -v $POSTGRES_DATA_DIR:/var/lib/postgresql/data \ - -p $DB_PORT:5432 \ - --env-file .env \ - postgres:16 - - # Wait for PostgreSQL to be ready - echo "Waiting for PostgreSQL to be ready..." - timeout 90s bash -c "until docker exec $POSTGRES_CONTAINER pg_isready ; do sleep 1 ; done" \ - && echo "Postgres is ready!" -else - echo "PostgreSQL container is already running" -fi - -# Database migrations -echo "Fix migrations from timestampd to sequential" -goose fix -dir .sqlc/migrations -echo "Run migrations" -goose -dir .sqlc/migrations postgres \ - "postgres://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME?sslmode=$DB_SSLMODE" up - -# Application deployment -echo "Building application Docker image..." -docker build -t $APP_NAME:latest . - -# Container management -echo "Stopping and removing existing container if present..." -docker stop $APP_NAME || true -docker rm $APP_NAME || true - -echo "Starting new application container..." -docker run -d \ - --name $APP_NAME \ - --env-file .env \ - --network=host --add-host=host.docker.internal:host-gateway \ - -v ~/dist:/root/static/dist \ - $APP_NAME:latest - -echo "Done: Preview Deployment"