From f2c50676777e63238cb01a513b7a05bed7fd5c75 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 07:26:48 -0500 Subject: [PATCH 001/160] added to .gitignore --- .github/deploy.yaml | 67 +++++++++ .gitignore | 1 + backend/k8s-manifest.yml | 288 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 356 insertions(+) create mode 100644 .github/deploy.yaml create mode 100644 backend/k8s-manifest.yml diff --git a/.github/deploy.yaml b/.github/deploy.yaml new file mode 100644 index 00000000..0746a096 --- /dev/null +++ b/.github/deploy.yaml @@ -0,0 +1,67 @@ +name: Deploy to Amazon EKS + +on: + push: + branches: + - cicd # Trigger deployment on the main branch + +env: + AWS_REGION: us-east-1 + EKS_CLUSTER_NAME: my-eks-cluster + ECR_REPOSITORY: my-app-repo + IMAGE_TAG: latest + SECRET_NAME: my-app-config + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + id-token: write # For OIDC auth (recommended) + contents: read + + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + run: | + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin .dkr.ecr.$AWS_REGION.amazonaws.com + + - name: Build and Push Docker Image + run: | + docker build -t $ECR_REPOSITORY:$IMAGE_TAG . + docker tag $ECR_REPOSITORY:$IMAGE_TAG .dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG + docker push .dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG + + # - name: Fetch Secrets from AWS Secrets Manager + # run: | + # SECRET_JSON=$(aws secretsmanager get-secret-value --secret-id $SECRET_NAME --query SecretString --output text) + # echo "$SECRET_JSON" > secret.json + + # - name: Create ConfigMap from AWS Secrets + # run: | + # APP_ENV=$(jq -r '.APP_ENV' secret.json) + # LOG_LEVEL=$(jq -r '.LOG_LEVEL' secret.json) + + # echo "apiVersion: v1 + # kind: ConfigMap + # metadata: + # name: my-app-config + # data: + # APP_ENV: \"$APP_ENV\" + # LOG_LEVEL: \"$LOG_LEVEL\" + # " > k8s-manifests/configmap.yaml + + - name: Configure kubectl + run: | + aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION + + - name: Deploy to EKS + run: | + kubectl apply -f k8s-manifests/ + kubectl rollout status deployment/my-app-deployment diff --git a/.gitignore b/.gitignore index 1f2d3eae..f0062910 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ # Environment variables .env .env.local +backend-config.yaml # Log files *.log diff --git a/backend/k8s-manifest.yml b/backend/k8s-manifest.yml new file mode 100644 index 00000000..69d69b66 --- /dev/null +++ b/backend/k8s-manifest.yml @@ -0,0 +1,288 @@ +# Ingress + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: indeq-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - http: + paths: + - path: /gateway + pathType: Prefix + backend: + service: + name: gateway-service + port: + number: 8080 + - path: /query + pathType: Prefix + backend: + service: + name: query-service + port: + number: 8080 + - path: /auth + pathType: Prefix + backend: + service: + name: authentication-service + port: + number: 8080 + - path: /ollama + pathType: Prefix + backend: + service: + name: ollama-service + port: + number: 11434 + +--- +# ConfigMap for environment variables +apiVersion: v1 +kind: ConfigMap +metadata: + name: backend-config +data: + POSTGRES_USER: your_user + POSTGRES_DB: your_db + POSTGRES_PASSWORD: your_password + +--- +# Gateway Service +apiVersion: v1 +kind: Service +metadata: + name: gateway-service +spec: + selector: + app: gateway + ports: + - port: 8080 + targetPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: gateway-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: gateway + template: + metadata: + labels: + app: gateway + spec: + containers: + - name: gateway + image: gateway-service + # imagePullPolicy: Never + ports: + - containerPort: 8080 + +--- +# Query Service +apiVersion: v1 +kind: Service +metadata: + name: query-service +spec: + selector: + app: query + ports: + - port: 8080 + targetPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: query-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: query + template: + metadata: + labels: + app: query + spec: + containers: + - name: query + image: query-service + # imagePullPolicy: Never + ports: + - containerPort: 8080 + +--- +# Authentication Service +apiVersion: v1 +kind: Service +metadata: + name: authentication-service +spec: + selector: + app: authentication + ports: + - port: 8080 + targetPort: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: authentication-deployment +spec: + replicas: 1 + selector: + matchLabels: + app: authentication + template: + metadata: + labels: + app: authentication + spec: + containers: + - name: authentication + image: authentication-service + # imagePullPolicy: Never + ports: + - containerPort: 8080 + +--- +# Ollama Service +apiVersion: v1 +kind: Service +metadata: + name: ollama-service +spec: + selector: + app: ollama + ports: + - port: 11434 + targetPort: 11434 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: ollama-deployment +spec: + replicas: 1 # Single replica since this is a stateful workload + selector: + matchLabels: + app: ollama + template: + metadata: + labels: + app: ollama + spec: + containers: + - name: ollama + image: ollama/ollama:latest + # imagePullPolicy: Never + ports: + - containerPort: 11434 + resources: + limits: + memory: "16Gi" + env: + - name: OLLAMA_NUM_PARALLEL + value: "4" + volumeMounts: + - name: ollama-models + mountPath: /root/.ollama + volumes: + - name: ollama-models + emptyDir: {} + +--- +# RabbitMQ Service +apiVersion: v1 +kind: Service +metadata: + name: rabbitmq-service +spec: + selector: + app: rabbitmq + ports: + - name: amqp + port: 5672 + targetPort: 5672 + - name: management + port: 15672 + targetPort: 15672 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: rabbitmq-deployment +spec: + replicas: 1 # Single replica since this is a stateful workload + selector: + matchLabels: + app: rabbitmq + template: + metadata: + labels: + app: rabbitmq + spec: + containers: + - name: rabbitmq + image: rabbitmq:3-management + # imagePullPolicy: Never + ports: + - containerPort: 5672 + - containerPort: 15672 + volumeMounts: + - name: rabbitmq-data + mountPath: /var/lib/rabbitmq + volumes: + - name: rabbitmq-data + emptyDir: {} + +--- +# PostgreSQL Service +apiVersion: v1 +kind: Service +metadata: + name: postgres-service +spec: + selector: + app: postgres + ports: + - port: 5432 + targetPort: 5432 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: postgres-deployment +spec: + replicas: 1 # Single replica since this is a stateful workload + selector: + matchLabels: + app: postgres + template: + metadata: + labels: + app: postgres + spec: + containers: + - name: postgres + image: postgres:latest + # imagePullPolicy: Never + ports: + - containerPort: 5432 + volumeMounts: + - name: postgres-data + mountPath: /var/lib/postgresql/data + envFrom: + - configMapRef: + name: backend-config # Reference the ConfigMap here + volumes: + - name: postgres-data + emptyDir: {} + From f4756632a80e10999c3709170a5a22f5f2ada41c Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 07:27:51 -0500 Subject: [PATCH 002/160] moved to workflow folder --- .github/{ => workflows}/deploy.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/deploy.yaml (100%) diff --git a/.github/deploy.yaml b/.github/workflows/deploy.yaml similarity index 100% rename from .github/deploy.yaml rename to .github/workflows/deploy.yaml From 85b610486c0d7ad0d374b8024bf2ef9c1794ca4b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 08:19:08 -0500 Subject: [PATCH 003/160] added ps1 script to add secrets from config.yaml and add params for deploy.yaml --- backend/setsecrets.ps1 | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 backend/setsecrets.ps1 diff --git a/backend/setsecrets.ps1 b/backend/setsecrets.ps1 new file mode 100644 index 00000000..710fd4f2 --- /dev/null +++ b/backend/setsecrets.ps1 @@ -0,0 +1,40 @@ +# Load the YAML file and extract the data section +$data = yq eval '.data | to_entries' .\backend-config.yaml + +# Check if any secrets were found +if ($data.Count -eq 0) { + Write-Host "No secrets found in backend-config.yaml." + exit +} + +# Loop through each secret +foreach ($secret in $data) { + # Extract the raw secret string + $rawSecret = $secret + + # Trim the string and extract key and value + $trimmedSecret = $rawSecret.Trim() + $keyValue = $trimmedSecret -replace '^- key: ', '' -split ': ' + + # Extract the key and value + $name = $keyValue[0].Trim() # This will be the key + $value = $keyValue[1].Trim() # This will be the value if it exists + + # Debug output to check the extracted values + Write-Host "Extracted secret: Name='$name', Value='$value'" + + # Check if the value is empty or commented out + if (-not $value -or $value -match '^\s*#') { + Write-Host "Warning: The value for secret '$name' is empty or commented out. Skipping..." + continue + } + + # Output the secret being set + Write-Host "Setting secret: $name" + + # Set the secret in GitHub + gh secret set $name --body $value + + # Confirm the secret was set + Write-Host "Secret $name has been set." +} \ No newline at end of file From c3e4a129e029b328d8e4a5a3bd82ed561ec7684a Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 08:22:01 -0500 Subject: [PATCH 004/160] created configmap from github secrets, applied config map in deploy stage --- .github/workflows/deploy.yaml | 70 +++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 23 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 0746a096..99ab9c56 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -3,14 +3,13 @@ name: Deploy to Amazon EKS on: push: branches: - - cicd # Trigger deployment on the main branch + - cicd # Trigger deployment on the cicd branch env: AWS_REGION: us-east-1 - EKS_CLUSTER_NAME: my-eks-cluster - ECR_REPOSITORY: my-app-repo + EKS_CLUSTER_NAME: backend-cluster + ECR_REPOSITORY: indeq/backend IMAGE_TAG: latest - SECRET_NAME: my-app-config jobs: deploy: @@ -38,24 +37,48 @@ jobs: docker tag $ECR_REPOSITORY:$IMAGE_TAG .dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG docker push .dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG - # - name: Fetch Secrets from AWS Secrets Manager - # run: | - # SECRET_JSON=$(aws secretsmanager get-secret-value --secret-id $SECRET_NAME --query SecretString --output text) - # echo "$SECRET_JSON" > secret.json - - # - name: Create ConfigMap from AWS Secrets - # run: | - # APP_ENV=$(jq -r '.APP_ENV' secret.json) - # LOG_LEVEL=$(jq -r '.LOG_LEVEL' secret.json) - - # echo "apiVersion: v1 - # kind: ConfigMap - # metadata: - # name: my-app-config - # data: - # APP_ENV: \"$APP_ENV\" - # LOG_LEVEL: \"$LOG_LEVEL\" - # " > k8s-manifests/configmap.yaml + - name: Create config.yaml with GitHub secrets + run: | + echo "apiVersion: v1 + kind: ConfigMap + metadata: + name: backend-config + data: + AUTH_PORT: \"\${{ secrets.AUTH_PORT }}\" + AUTH_ADDRESS: \"\${{ secrets.AUTH_ADDRESS }}\" + QUERY_PORT: \"\${{ secrets.QUERY_PORT }}\" + QUERY_ADDRESS: \"\${{ secrets.QUERY_ADDRESS }}\" + VECTOR_PORT: \"\${{ secrets.VECTOR_PORT }}\" + VECTOR_ADDRESS: \"\${{ secrets.VECTOR_ADDRESS }}\" + GATEWAY_ADDRESS: \"\${{ secrets.GATEWAY_ADDRESS }}\" + DATABASE_URL: \"\${{ secrets.DATABASE_URL }}\" + POSTGRES_USER: \"\${{ secrets.POSTGRES_USER }}\" + POSTGRES_PASSWORD: \"\${{ secrets.POSTGRES_PASSWORD }}\" + POSTGRES_DB: \"\${{ secrets.POSTGRES_DB }}\" + JWT_SECRET: \"\${{ secrets.JWT_SECRET }}\" + ARGON2_MEMORY: \"\${{ secrets.ARGON2_MEMORY }}\" + ARGON2_ITERATIONS: \"\${{ secrets.ARGON2_ITERATIONS }}\" + ARGON2_PARALLELISM: \"\${{ secrets.ARGON2_PARALLELISM }}\" + ARGON2_SALT_LENGTH: \"\${{ secrets.ARGON2_SALT_LENGTH }}\" + ARGON2_KEY_LENGTH: \"\${{ secrets.ARGON2_KEY_LENGTH }}\" + MIN_PASSWORD_LENGTH: \"\${{ secrets.MIN_PASSWORD_LENGTH }}\" + MAX_PASSWORD_LENGTH: \"\${{ secrets.MAX_PASSWORD_LENGTH }}\" + MAX_EMAIL_LENGTH: \"\${{ secrets.MAX_EMAIL_LENGTH }}\" + RABBITMQ_URL: \"\${{ secrets.RABBITMQ_URL }}\" + RABBITMQ_DEFAULT_USER: \"\${{ secrets.RABBITMQ_DEFAULT_USER }}\" + RABBITMQ_DEFAULT_PASS: \"\${{ secrets.RABBITMQ_DEFAULT_PASS }}\" + RABBITMQ_LOGS: \"\${{ secrets.RABBITMQ_LOGS }}\" + OLLAMA_URL: \"\${{ secrets.OLLAMA_URL }}\" + LLM_MODEL: \"\${{ secrets.LLM_MODEL }}\" + ZILLIZ_ADDRESS: \"\${{ secrets.ZILLIZ_ADDRESS }}\" + ZILLIZ_API_KEY: \"\${{ secrets.ZILLIZ_API_KEY }}\" + CA_CRT: \"\${{ secrets.CA_CRT }}\" + QUERY_CRT: \"\${{ secrets.QUERY_CRT }}\" + QUERY_KEY: \"\${{ secrets.QUERY_KEY }}\" + AUTH_CRT: \"\${{ secrets.AUTH_CRT }}\" + AUTH_KEY: \"\${{ secrets.AUTH_KEY }}\" + ALLOWED_CLIENT_IP: \"\${{ secrets.ALLOWED_CLIENT_IP }}\" + " > configmap.yaml - name: Configure kubectl run: | @@ -63,5 +86,6 @@ jobs: - name: Deploy to EKS run: | - kubectl apply -f k8s-manifests/ + kubectl apply -f k8s-manifests.yml + kubectl apply -f configmap.yaml kubectl rollout status deployment/my-app-deployment From c9bcbdd6be593e21e60ba578b9bac8b3c42b2c4f Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 08:32:19 -0500 Subject: [PATCH 005/160] added more github secrets for aws login --- .github/workflows/deploy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 99ab9c56..770df267 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -25,6 +25,8 @@ jobs: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR From f74a01245f4fd4c892381b53afeed5728ac12270 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 08:49:11 -0500 Subject: [PATCH 006/160] updated script, fixed syntax error in param naming in configuration stage --- .github/workflows/deploy.yaml | 6 +++--- backend/setsecrets.ps1 | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 770df267..cf1febf9 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -25,9 +25,9 @@ jobs: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_REGION: ${{ env.AWS_REGION }} - name: Login to Amazon ECR run: | diff --git a/backend/setsecrets.ps1 b/backend/setsecrets.ps1 index 710fd4f2..3e1ecc9f 100644 --- a/backend/setsecrets.ps1 +++ b/backend/setsecrets.ps1 @@ -1,6 +1,10 @@ # Load the YAML file and extract the data section $data = yq eval '.data | to_entries' .\backend-config.yaml +# Output the raw data for debugging +Write-Host "Raw data extracted from YAML:" +Write-Host $data + # Check if any secrets were found if ($data.Count -eq 0) { Write-Host "No secrets found in backend-config.yaml." @@ -16,13 +20,19 @@ foreach ($secret in $data) { $trimmedSecret = $rawSecret.Trim() $keyValue = $trimmedSecret -replace '^- key: ', '' -split ': ' + # Check if the keyValue array has the expected number of elements + if ($keyValue.Count -lt 2) { + Write-Host "Warning: The entry '$rawSecret' does not contain a valid key-value pair. Skipping..." + continue + } + # Extract the key and value $name = $keyValue[0].Trim() # This will be the key $value = $keyValue[1].Trim() # This will be the value if it exists # Debug output to check the extracted values + $value = $value -replace '^"|"$', '' # Remove extra outer double quotes from the value Write-Host "Extracted secret: Name='$name', Value='$value'" - # Check if the value is empty or commented out if (-not $value -or $value -match '^\s*#') { Write-Host "Warning: The value for secret '$name' is empty or commented out. Skipping..." From 0b0cfaff90cce5d7f7f8b620daa56c4b05e9074f Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 08:55:14 -0500 Subject: [PATCH 007/160] changed backed --- .github/workflows/deploy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index cf1febf9..770df267 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -25,9 +25,9 @@ jobs: - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - AWS_REGION: ${{ env.AWS_REGION }} + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} - name: Login to Amazon ECR run: | From 1fccff8c6edd9f428abe99dffe067a4c6bd1302f Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 09:53:00 -0500 Subject: [PATCH 008/160] added aws account id --- backend/setsecrets.ps1 | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/backend/setsecrets.ps1 b/backend/setsecrets.ps1 index 3e1ecc9f..fd15448d 100644 --- a/backend/setsecrets.ps1 +++ b/backend/setsecrets.ps1 @@ -1,9 +1,5 @@ # Load the YAML file and extract the data section -$data = yq eval '.data | to_entries' .\backend-config.yaml - -# Output the raw data for debugging -Write-Host "Raw data extracted from YAML:" -Write-Host $data +$data = yq eval '.' .\backend-config.yaml # Check if any secrets were found if ($data.Count -eq 0) { @@ -18,7 +14,7 @@ foreach ($secret in $data) { # Trim the string and extract key and value $trimmedSecret = $rawSecret.Trim() - $keyValue = $trimmedSecret -replace '^- key: ', '' -split ': ' + $keyValue = $trimmedSecret -split ': ' # Check if the keyValue array has the expected number of elements if ($keyValue.Count -lt 2) { @@ -39,12 +35,7 @@ foreach ($secret in $data) { continue } - # Output the secret being set - Write-Host "Setting secret: $name" - # Set the secret in GitHub gh secret set $name --body $value - # Confirm the secret was set - Write-Host "Secret $name has been set." } \ No newline at end of file From 4dc7c5bb872dbef4510fb4a7c9feac47945393d0 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 09:56:15 -0500 Subject: [PATCH 009/160] added aws account id --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 770df267..e4bcca04 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -31,7 +31,7 @@ jobs: - name: Login to Amazon ECR run: | - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin .dkr.ecr.$AWS_REGION.amazonaws.com + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - name: Build and Push Docker Image run: | From 1381b9da8075ae699195179a10534353afa22085 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:15:28 -0500 Subject: [PATCH 010/160] modified tagging/image pushing process with custom repos --- backend/testing.ps1 | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 backend/testing.ps1 diff --git a/backend/testing.ps1 b/backend/testing.ps1 new file mode 100644 index 00000000..dd9a2996 --- /dev/null +++ b/backend/testing.ps1 @@ -0,0 +1,9 @@ +# Get the list of services defined in the docker-compose.yml +$services = docker-compose config --services + +# Loop through each service +foreach ($service in $services) { + # Tag the image + Write-Host "$service" + +} From dc0e19072fc13ee9f71b0c135c0eee7250ff7592 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:16:39 -0500 Subject: [PATCH 011/160] added dir --- .github/workflows/deploy.yaml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index e4bcca04..ed033f72 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -8,8 +8,8 @@ on: env: AWS_REGION: us-east-1 EKS_CLUSTER_NAME: backend-cluster - ECR_REPOSITORY: indeq/backend - IMAGE_TAG: latest + ECR_REPOSITORY_NAMESPACE: indeq + IMAGE_TAG: ${{ github.run_id }} jobs: deploy: @@ -33,11 +33,22 @@ jobs: run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - - name: Build and Push Docker Image - run: | - docker build -t $ECR_REPOSITORY:$IMAGE_TAG . - docker tag $ECR_REPOSITORY:$IMAGE_TAG .dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG - docker push .dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY:$IMAGE_TAG + - name: Build images with Docker Compose + run: | + docker-compose build + + - name: Tag and Push images to ECR + run: | + cd backend + # Loop through each service defined in the docker-compose.yml + for service in $(docker-compose config --services); do + if [[ "$service" != "passwordDB" && "$service" != "ollama" ]]; then # ollama and db is not pushed to ECR + # Tag the image + docker tag $service:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG + # Push the image to ECR + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG + fi + done - name: Create config.yaml with GitHub secrets run: | From 3d943ac29a6178c1ce6d0ce712a2914f35f8dc6f Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:17:51 -0500 Subject: [PATCH 012/160] indentation --- .github/workflows/deploy.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ed033f72..c7247358 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -37,18 +37,18 @@ jobs: run: | docker-compose build - - name: Tag and Push images to ECR - run: | - cd backend - # Loop through each service defined in the docker-compose.yml - for service in $(docker-compose config --services); do - if [[ "$service" != "passwordDB" && "$service" != "ollama" ]]; then # ollama and db is not pushed to ECR - # Tag the image - docker tag $service:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG - # Push the image to ECR - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG - fi - done + - name: Tag and Push images to ECR + run: | + cd backend + # Loop through each service defined in the docker-compose.yml + for service in $(docker-compose config --services); do + if [[ "$service" != "passwordDB" && "$service" != "ollama" ]]; then # ollama and db is not pushed to ECR + # Tag the image + docker tag $service:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG + # Push the image to ECR + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG + fi + done - name: Create config.yaml with GitHub secrets run: | From 0c730df56399b8af43b74ef48053969675d6ee44 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:19:55 -0500 Subject: [PATCH 013/160] syntax + removed script file --- .github/workflows/deploy.yaml | 7 +++---- backend/testing.ps1 | 9 --------- 2 files changed, 3 insertions(+), 13 deletions(-) delete mode 100644 backend/testing.ps1 diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index c7247358..6bb8a86c 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -34,8 +34,8 @@ jobs: aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - name: Build images with Docker Compose - run: | - docker-compose build + run: | + docker-compose build - name: Tag and Push images to ECR run: | @@ -99,6 +99,5 @@ jobs: - name: Deploy to EKS run: | - kubectl apply -f k8s-manifests.yml + kubectl apply -f backend/k8s-manifest.yml kubectl apply -f configmap.yaml - kubectl rollout status deployment/my-app-deployment diff --git a/backend/testing.ps1 b/backend/testing.ps1 deleted file mode 100644 index dd9a2996..00000000 --- a/backend/testing.ps1 +++ /dev/null @@ -1,9 +0,0 @@ -# Get the list of services defined in the docker-compose.yml -$services = docker-compose config --services - -# Loop through each service -foreach ($service in $services) { - # Tag the image - Write-Host "$service" - -} From c7933a6afe9652dfa36b4a11a4965c35ebb25f9b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:21:34 -0500 Subject: [PATCH 014/160] added steps to install docker compose --- .github/workflows/deploy.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 6bb8a86c..ae012f40 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -22,6 +22,14 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Install Docker Compose + run: | + sudo apt-get update + sudo apt-get install -y docker-compose + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: From ecc1ebf3cc1d423d67fe3b92edf8c2e79ebdac6b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:24:17 -0500 Subject: [PATCH 015/160] added .yaml to file ending --- .github/workflows/deploy.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ae012f40..5385f993 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -48,8 +48,9 @@ jobs: - name: Tag and Push images to ECR run: | cd backend + ls -la # Loop through each service defined in the docker-compose.yml - for service in $(docker-compose config --services); do + for service in $(docker-compose.yaml config --services); do if [[ "$service" != "passwordDB" && "$service" != "ollama" ]]; then # ollama and db is not pushed to ECR # Tag the image docker tag $service:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG From d4b75c5e8f537043fd30b06a8e9e12ce7bdf537e Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:40:37 -0500 Subject: [PATCH 016/160] added dir to docker-compose step --- .github/workflows/deploy.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 5385f993..1eb1815e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -47,10 +47,8 @@ jobs: - name: Tag and Push images to ECR run: | - cd backend - ls -la # Loop through each service defined in the docker-compose.yml - for service in $(docker-compose.yaml config --services); do + for service in $(backend/docker-compose.yaml config --services); do if [[ "$service" != "passwordDB" && "$service" != "ollama" ]]; then # ollama and db is not pushed to ECR # Tag the image docker tag $service:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG From 932c93bbf59b79343b032a2e9b76f18b88f6bb87 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 13:53:45 -0500 Subject: [PATCH 017/160] added file dir checking and removed docker compose --- .github/workflows/deploy.yaml | 36 +++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1eb1815e..8f104af4 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -21,15 +21,15 @@ jobs: steps: - name: Checkout Repository uses: actions/checkout@v3 + + - name: Check File Directory + run: | + echo "Checking file directory..." + ls -la - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - - name: Install Docker Compose - run: | - sudo apt-get update - sudo apt-get install -y docker-compose - - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 with: @@ -41,22 +41,22 @@ jobs: run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - - name: Build images with Docker Compose - run: | - docker-compose build - - name: Tag and Push images to ECR run: | - # Loop through each service defined in the docker-compose.yml - for service in $(backend/docker-compose.yaml config --services); do - if [[ "$service" != "passwordDB" && "$service" != "ollama" ]]; then # ollama and db is not pushed to ECR - # Tag the image - docker tag $service:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG - # Push the image to ECR - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/$service:$IMAGE_TAG - fi - done + # Tagging each service and pushing to ECR + docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + + docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + + docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + + docker tag rabbitmq:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/rabbitmq:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/rabbitmq:$IMAGE_TAG + - name: Create config.yaml with GitHub secrets run: | echo "apiVersion: v1 From 073f3ac762fdd7bd835546d1cdcdd0bada125730 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 14:01:10 -0500 Subject: [PATCH 018/160] added docker build step --- .github/workflows/deploy.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 8f104af4..67c1a21e 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -21,7 +21,7 @@ jobs: steps: - name: Checkout Repository uses: actions/checkout@v3 - + - name: Check File Directory run: | echo "Checking file directory..." @@ -44,16 +44,19 @@ jobs: - name: Tag and Push images to ECR run: | - # Tagging each service and pushing to ECR + docker build -t authentication:$IMAGE_TAG -f backend/Dockerfile . docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker build -t query:$IMAGE_TAG -f backend/Dockerfile . docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker build -t gateway:$IMAGE_TAG -f backend/Dockerfile . docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker build -t rabbitmq:$IMAGE_TAG -f backend/Dockerfile . docker tag rabbitmq:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/rabbitmq:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/rabbitmq:$IMAGE_TAG From b6b8068130371774313ace96e17260c8847484d5 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 14:04:45 -0500 Subject: [PATCH 019/160] Changed path for dockerfile --- .github/workflows/deploy.yaml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 67c1a21e..125b1e3a 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -44,22 +44,18 @@ jobs: - name: Tag and Push images to ECR run: | - docker build -t authentication:$IMAGE_TAG -f backend/Dockerfile . + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile . docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker build -t query:$IMAGE_TAG -f backend/Dockerfile . + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile . docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker build -t gateway:$IMAGE_TAG -f backend/Dockerfile . + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile . docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - docker build -t rabbitmq:$IMAGE_TAG -f backend/Dockerfile . - docker tag rabbitmq:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/rabbitmq:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/rabbitmq:$IMAGE_TAG - - name: Create config.yaml with GitHub secrets run: | echo "apiVersion: v1 From 0b4a16c8032fb64e30547a0942f66d7493ab09de Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 18:50:27 -0500 Subject: [PATCH 020/160] added make gen --- .github/workflows/deploy.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 125b1e3a..36994c2f 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -26,6 +26,11 @@ jobs: run: | echo "Checking file directory..." ls -la + + - name: Generate Code + run: | + cd backend/common + make gen - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 From deb8e91a1547bc3e8309fe2c9fe412a809560fda Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 18:58:26 -0500 Subject: [PATCH 021/160] changed path --- .github/workflows/deploy.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 36994c2f..62979074 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -26,11 +26,10 @@ jobs: run: | echo "Checking file directory..." ls -la - + - name: Generate Code run: | - cd backend/common - make gen + make backend/common/gen - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 From ef7df18f7958ea6e4372f1d2a015bccea17ca5d6 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:04:30 -0500 Subject: [PATCH 022/160] changed syntax --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 62979074..53372d4b 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -29,7 +29,7 @@ jobs: - name: Generate Code run: | - make backend/common/gen + make gen backend/common - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 From 25c7e893acc25c9a88760a646b67dc3752462ab7 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:08:32 -0500 Subject: [PATCH 023/160] added debugging statements --- .github/workflows/deploy.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 53372d4b..a78d2e24 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -26,10 +26,13 @@ jobs: run: | echo "Checking file directory..." ls -la + cd backend/common + ls -la - name: Generate Code run: | - make gen backend/common + cd backend/common + make gen - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 From 1e006b5eae47274b2374d2152de4d3865d72c947 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:13:08 -0500 Subject: [PATCH 024/160] changed path and make gen to be on same line --- .github/workflows/deploy.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a78d2e24..c402336c 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -26,13 +26,10 @@ jobs: run: | echo "Checking file directory..." ls -la - cd backend/common - ls -la - name: Generate Code run: | - cd backend/common - make gen + cd backend/common && make gen - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 From 73941192935f4a5ddae686aca03d8df63c85a33c Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:15:05 -0500 Subject: [PATCH 025/160] installed protocol buffers --- .github/workflows/deploy.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index c402336c..372a6da0 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -27,6 +27,11 @@ jobs: echo "Checking file directory..." ls -la + - name: Install Protocol Buffers Compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler + - name: Generate Code run: | cd backend/common && make gen From cfcbf338b2e5e764e78cedfd81cfa634089a5703 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:16:57 -0500 Subject: [PATCH 026/160] added go and protoc-gen-go --- .github/workflows/deploy.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 372a6da0..7101d39b 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -32,6 +32,16 @@ jobs: sudo apt-get update sudo apt-get install -y protobuf-compiler + - name: Install Go + run: | + sudo apt-get update + sudo apt-get install -y golang-go + + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "$GOPATH/bin" >> $GITHUB_ENV + - name: Generate Code run: | cd backend/common && make gen From 75ab7d3c33abbe739397daa569b0f6572ac634c3 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:18:14 -0500 Subject: [PATCH 027/160] removed protoc-gen-go --- .github/workflows/deploy.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 7101d39b..cc05f36b 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -37,11 +37,6 @@ jobs: sudo apt-get update sudo apt-get install -y golang-go - - name: Install protoc-gen-go - run: | - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "$GOPATH/bin" >> $GITHUB_ENV - - name: Generate Code run: | cd backend/common && make gen From e1e96558152caa292c075a1e1e732780b977d103 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:20:22 -0500 Subject: [PATCH 028/160] fixed path --- .github/workflows/deploy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index cc05f36b..39ecf289 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -37,6 +37,12 @@ jobs: sudo apt-get update sudo apt-get install -y golang-go + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "$GOPATH/bin" >> $GITHUB_ENV + - name: Generate Code run: | cd backend/common && make gen From 4d6e31ebe451f2afbe03715beabaafda3f6c88b4 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:24:21 -0500 Subject: [PATCH 029/160] added debugging statements --- .github/workflows/deploy.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 39ecf289..465776e0 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -40,7 +40,6 @@ jobs: - name: Install protoc-gen-go run: | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV echo "$GOPATH/bin" >> $GITHUB_ENV - name: Generate Code @@ -127,3 +126,8 @@ jobs: run: | kubectl apply -f backend/k8s-manifest.yml kubectl apply -f configmap.yaml + + - name: Debug Environment Variables + run: | + echo "Current GOPATH: $(go env GOPATH)" + echo "Current PATH: $PATH" From 2acddb1f218a657827e2b121ae231b3c683a7354 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:27:21 -0500 Subject: [PATCH 030/160] change protoc-gen-go path --- .github/workflows/deploy.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 465776e0..5fee9d7c 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -40,7 +40,12 @@ jobs: - name: Install protoc-gen-go run: | go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "$GOPATH/bin" >> $GITHUB_ENV + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Check protoc-gen-go Installation + run: | + protoc-gen-go --version || echo "protoc-gen-go not found" - name: Generate Code run: | From b9e305ffb1b57c1adbe8198f67f33d88e7998a99 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:29:31 -0500 Subject: [PATCH 031/160] add grpc download --- .github/workflows/deploy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 5fee9d7c..4e851066 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -43,6 +43,12 @@ jobs: echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + - name: Install protoc-gen-go-grpc + run: | + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + - name: Check protoc-gen-go Installation run: | protoc-gen-go --version || echo "protoc-gen-go not found" From cdb98d99acc3e87594ac2e6322eb4cd3cd9b3cfd Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 19:51:14 -0500 Subject: [PATCH 032/160] add abs path --- .github/workflows/deploy.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 4e851066..305434eb 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -74,18 +74,17 @@ jobs: - name: Tag and Push images to ECR run: | - docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile . + docker build -t authentication:$IMAGE_TAG -f $GITHUB_WORKSPACE/backend/authentication/Dockerfile $GITHUB_WORKSPACE docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile . + docker build -t query:$IMAGE_TAG -f $GITHUB_WORKSPACE/backend/query/Dockerfile $GITHUB_WORKSPACE docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile . + docker build -t gateway:$IMAGE_TAG -f $GITHUB_WORKSPACE/backend/gateway/Dockerfile $GITHUB_WORKSPACE docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - - name: Create config.yaml with GitHub secrets run: | echo "apiVersion: v1 From 22220ac99009312136482e6c0eac6142ad242b39 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 20:02:56 -0500 Subject: [PATCH 033/160] fixed up dir --- .github/workflows/deploy.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 305434eb..94d33b6b 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v3 + uses: actions/checkout@v2 - name: Check File Directory run: | @@ -71,20 +71,20 @@ jobs: run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - - name: Tag and Push images to ECR + - name: Build and Push Docker Images run: | - - docker build -t authentication:$IMAGE_TAG -f $GITHUB_WORKSPACE/backend/authentication/Dockerfile $GITHUB_WORKSPACE + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker build -t query:$IMAGE_TAG -f $GITHUB_WORKSPACE/backend/query/Dockerfile $GITHUB_WORKSPACE + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker build -t gateway:$IMAGE_TAG -f $GITHUB_WORKSPACE/backend/gateway/Dockerfile $GITHUB_WORKSPACE + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + - name: Create config.yaml with GitHub secrets run: | echo "apiVersion: v1 From e4b97b887a1a67a816a186dc13992cd2b14c8555 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 20:15:59 -0500 Subject: [PATCH 034/160] added script to create .env --- .github/workflows/deploy.yaml | 5 +++ generate_env.sh | 69 +++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 generate_env.sh diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 94d33b6b..ea3308af 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -52,6 +52,11 @@ jobs: - name: Check protoc-gen-go Installation run: | protoc-gen-go --version || echo "protoc-gen-go not found" + + - name: Generate .env Files + run: | + chmod +x ./generate_env.sh + ./generate_env.sh - name: Generate Code run: | diff --git a/generate_env.sh b/generate_env.sh new file mode 100644 index 00000000..11f61e22 --- /dev/null +++ b/generate_env.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Create .env files in the specified directories +echo "Creating .env files..." + +# Define the directories +declare -a dirs=("backend/gateway" "backend/authentication" "backend/query") + +# Loop through each directory and create the .env file +for dir in "${dirs[@]}"; do + echo "Creating .env in $dir" + { + echo "AUTH_PORT=${{ secrets.AUTH_PORT }}" + echo "AUTH_ADDRESS=${{ secrets.AUTH_ADDRESS }}" + echo "QUERY_PORT=${{ secrets.QUERY_PORT }}" + echo "QUERY_ADDRESS=${{ secrets.QUERY_ADDRESS }}" + echo "VECTOR_PORT=${{ secrets.VECTOR_PORT }}" + echo "VECTOR_ADDRESS=${{ secrets.VECTOR_ADDRESS }}" + echo "GATEWAY_ADDRESS=${{ secrets.GATEWAY_ADDRESS }}" + + # Database parameters + echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" + echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" + echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" + echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" + + echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" + + # Argon2 Parameters + echo "ARGON2_MEMORY=${{ secrets.ARGON2_MEMORY }}" + echo "ARGON2_ITERATIONS=${{ secrets.ARGON2_ITERATIONS }}" + echo "ARGON2_PARALLELISM=${{ secrets.ARGON2_PARALLELISM }}" + echo "ARGON2_SALT_LENGTH=${{ secrets.ARGON2_SALT_LENGTH }}" + echo "ARGON2_KEY_LENGTH=${{ secrets.ARGON2_KEY_LENGTH }}" + + # Password and Email Constraints + echo "MIN_PASSWORD_LENGTH=${{ secrets.MIN_PASSWORD_LENGTH }}" + echo "MAX_PASSWORD_LENGTH=${{ secrets.MAX_PASSWORD_LENGTH }}" + echo "MAX_EMAIL_LENGTH=${{ secrets.MAX_EMAIL_LENGTH }}" + + # RabbitMQ parameters + echo "RABBITMQ_URL=${{ secrets.RABBITMQ_URL }}" + echo "RABBITMQ_DEFAULT_USER=${{ secrets.RABBITMQ_DEFAULT_USER }}" + echo "RABBITMQ_DEFAULT_PASS=${{ secrets.RABBITMQ_DEFAULT_PASS }}" + echo "RABBITMQ_LOGS=${{ secrets.RABBITMQ_LOGS }}" + + # Ollama parameters + echo "OLLAMA_URL=${{ secrets.OLLAMA_URL }}" + echo "LLM_MODEL=${{ secrets.LLM_MODEL }}" + + # Zilliz parameters + echo "ZILLIZ_ADDRESS=${{ secrets.ZILLIZ_ADDRESS }}" + echo "ZILLIZ_API_KEY=${{ secrets.ZILLIZ_API_KEY }}" + + # TLS parameters in base 64 + echo "CA_CRT=${{ secrets.CA_CRT }}" + echo "QUERY_CRT=${{ secrets.QUERY_CRT }}" + echo "QUERY_KEY=${{ secrets.QUERY_KEY }}" + echo "AUTH_CRT=${{ secrets.AUTH_CRT }}" + echo "AUTH_KEY=${{ secrets.AUTH_KEY }}" + echo "GATEWAY_CRT=${{ secrets.GATEWAY_CRT }}" + echo "GATEWAY_KEY=${{ secrets.GATEWAY_KEY }}" + + # CORS parameters + echo "ALLOWED_CLIENT_IP=${{ secrets.ALLOWED_CLIENT_IP }}" + } > "$dir/.env" +done + +echo ".env files created successfully." \ No newline at end of file From 4c221d946a71866f814e7fd7b0b79d754d256aa1 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 24 Feb 2025 20:17:08 -0500 Subject: [PATCH 035/160] syntax --- .github/workflows/deploy.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ea3308af..1bbd3d60 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -52,11 +52,11 @@ jobs: - name: Check protoc-gen-go Installation run: | protoc-gen-go --version || echo "protoc-gen-go not found" - + - name: Generate .env Files - run: | - chmod +x ./generate_env.sh - ./generate_env.sh + run: | + chmod +x ./generate_env.sh + ./generate_env.sh - name: Generate Code run: | From 77df8dd23b831e4aafc0677115e613f685b9a601 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 3 Mar 2025 14:19:19 -0500 Subject: [PATCH 036/160] added debugging statements to check env file gen --- .github/workflows/deploy.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 1bbd3d60..8dc8fb55 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -58,6 +58,15 @@ jobs: chmod +x ./generate_env.sh ./generate_env.sh + - name: Print Files in Directories + run: | + echo "Files in backend/gateway:" + ls -la backend/gateway + echo "Files in backend/authentication:" + ls -la backend/authentication + echo "Files in backend/query:" + ls -la backend/query + - name: Generate Code run: | cd backend/common && make gen From 39b9dd29582bbaeba90c96f64be874b874b45ab9 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 3 Mar 2025 14:22:02 -0500 Subject: [PATCH 037/160] added config folder --- generate_env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generate_env.sh b/generate_env.sh index 11f61e22..aaa36aeb 100644 --- a/generate_env.sh +++ b/generate_env.sh @@ -4,7 +4,7 @@ echo "Creating .env files..." # Define the directories -declare -a dirs=("backend/gateway" "backend/authentication" "backend/query") +declare -a dirs=("backend/gateway" "backend/authentication" "backend/query", "backend/common/config") # Loop through each directory and create the .env file for dir in "${dirs[@]}"; do From 3ebc205d22087320a1a55fa6ef3b34116234d9b8 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 3 Mar 2025 14:32:59 -0500 Subject: [PATCH 038/160] inject env into bash environment to generate .env --- .github/workflows/deploy.yaml | 40 ++++++++++++++++++- generate_env.sh | 74 +++++++++++++++++------------------ 2 files changed, 76 insertions(+), 38 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 8dc8fb55..ab1d4631 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -54,6 +54,44 @@ jobs: protoc-gen-go --version || echo "protoc-gen-go not found" - name: Generate .env Files + env: + AUTH_PORT: ${{ secrets.AUTH_PORT }} + AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + QUERY_PORT: ${{ secrets.QUERY_PORT }} + QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + CA_CRT: ${{ secrets.CA_CRT }} + QUERY_CRT: ${{ secrets.QUERY_CRT }} + QUERY_KEY: ${{ secrets.QUERY_KEY }} + AUTH_CRT: ${{ secrets.AUTH_CRT }} + AUTH_KEY: ${{ secrets.AUTH_KEY }} + GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + run: | chmod +x ./generate_env.sh ./generate_env.sh @@ -66,7 +104,7 @@ jobs: ls -la backend/authentication echo "Files in backend/query:" ls -la backend/query - + - name: Generate Code run: | cd backend/common && make gen diff --git a/generate_env.sh b/generate_env.sh index aaa36aeb..c86f4f13 100644 --- a/generate_env.sh +++ b/generate_env.sh @@ -4,65 +4,65 @@ echo "Creating .env files..." # Define the directories -declare -a dirs=("backend/gateway" "backend/authentication" "backend/query", "backend/common/config") +declare -a dirs=("backend/gateway" "backend/authentication" "backend/query" "backend/common/config") # Loop through each directory and create the .env file for dir in "${dirs[@]}"; do echo "Creating .env in $dir" { - echo "AUTH_PORT=${{ secrets.AUTH_PORT }}" - echo "AUTH_ADDRESS=${{ secrets.AUTH_ADDRESS }}" - echo "QUERY_PORT=${{ secrets.QUERY_PORT }}" - echo "QUERY_ADDRESS=${{ secrets.QUERY_ADDRESS }}" - echo "VECTOR_PORT=${{ secrets.VECTOR_PORT }}" - echo "VECTOR_ADDRESS=${{ secrets.VECTOR_ADDRESS }}" - echo "GATEWAY_ADDRESS=${{ secrets.GATEWAY_ADDRESS }}" + echo "AUTH_PORT=${AUTH_PORT}" + echo "AUTH_ADDRESS=${AUTH_ADDRESS}" + echo "QUERY_PORT=${QUERY_PORT}" + echo "QUERY_ADDRESS=${QUERY_ADDRESS}" + echo "VECTOR_PORT=${VECTOR_PORT}" + echo "VECTOR_ADDRESS=${VECTOR_ADDRESS}" + echo "GATEWAY_ADDRESS=${GATEWAY_ADDRESS}" # Database parameters - echo "DATABASE_URL=${{ secrets.DATABASE_URL }}" - echo "POSTGRES_USER=${{ secrets.POSTGRES_USER }}" - echo "POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }}" - echo "POSTGRES_DB=${{ secrets.POSTGRES_DB }}" + echo "DATABASE_URL=${DATABASE_URL}" + echo "POSTGRES_USER=${POSTGRES_USER}" + echo "POSTGRES_PASSWORD=${POSTGRES_PASSWORD}" + echo "POSTGRES_DB=${POSTGRES_DB}" - echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" + echo "JWT_SECRET=${JWT_SECRET}" # Argon2 Parameters - echo "ARGON2_MEMORY=${{ secrets.ARGON2_MEMORY }}" - echo "ARGON2_ITERATIONS=${{ secrets.ARGON2_ITERATIONS }}" - echo "ARGON2_PARALLELISM=${{ secrets.ARGON2_PARALLELISM }}" - echo "ARGON2_SALT_LENGTH=${{ secrets.ARGON2_SALT_LENGTH }}" - echo "ARGON2_KEY_LENGTH=${{ secrets.ARGON2_KEY_LENGTH }}" + echo "ARGON2_MEMORY=${ARGON2_MEMORY}" + echo "ARGON2_ITERATIONS=${ARGON2_ITERATIONS}" + echo "ARGON2_PARALLELISM=${ARGON2_PARALLELISM}" + echo "ARGON2_SALT_LENGTH=${ARGON2_SALT_LENGTH}" + echo "ARGON2_KEY_LENGTH=${ARGON2_KEY_LENGTH}" # Password and Email Constraints - echo "MIN_PASSWORD_LENGTH=${{ secrets.MIN_PASSWORD_LENGTH }}" - echo "MAX_PASSWORD_LENGTH=${{ secrets.MAX_PASSWORD_LENGTH }}" - echo "MAX_EMAIL_LENGTH=${{ secrets.MAX_EMAIL_LENGTH }}" + echo "MIN_PASSWORD_LENGTH=${MIN_PASSWORD_LENGTH}" + echo "MAX_PASSWORD_LENGTH=${MAX_PASSWORD_LENGTH}" + echo "MAX_EMAIL_LENGTH=${MAX_EMAIL_LENGTH}" # RabbitMQ parameters - echo "RABBITMQ_URL=${{ secrets.RABBITMQ_URL }}" - echo "RABBITMQ_DEFAULT_USER=${{ secrets.RABBITMQ_DEFAULT_USER }}" - echo "RABBITMQ_DEFAULT_PASS=${{ secrets.RABBITMQ_DEFAULT_PASS }}" - echo "RABBITMQ_LOGS=${{ secrets.RABBITMQ_LOGS }}" + echo "RABBITMQ_URL=${RABBITMQ_URL}" + echo "RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}" + echo "RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}" + echo "RABBITMQ_LOGS=${RABBITMQ_LOGS}" # Ollama parameters - echo "OLLAMA_URL=${{ secrets.OLLAMA_URL }}" - echo "LLM_MODEL=${{ secrets.LLM_MODEL }}" + echo "OLLAMA_URL=${OLLAMA_URL}" + echo "LLM_MODEL=${LLM_MODEL}" # Zilliz parameters - echo "ZILLIZ_ADDRESS=${{ secrets.ZILLIZ_ADDRESS }}" - echo "ZILLIZ_API_KEY=${{ secrets.ZILLIZ_API_KEY }}" + echo "ZILLIZ_ADDRESS=${ZILLIZ_ADDRESS}" + echo "ZILLIZ_API_KEY=${ZILLIZ_API_KEY}" # TLS parameters in base 64 - echo "CA_CRT=${{ secrets.CA_CRT }}" - echo "QUERY_CRT=${{ secrets.QUERY_CRT }}" - echo "QUERY_KEY=${{ secrets.QUERY_KEY }}" - echo "AUTH_CRT=${{ secrets.AUTH_CRT }}" - echo "AUTH_KEY=${{ secrets.AUTH_KEY }}" - echo "GATEWAY_CRT=${{ secrets.GATEWAY_CRT }}" - echo "GATEWAY_KEY=${{ secrets.GATEWAY_KEY }}" + echo "CA_CRT=${CA_CRT}" + echo "QUERY_CRT=${QUERY_CRT}" + echo "QUERY_KEY=${QUERY_KEY}" + echo "AUTH_CRT=${AUTH_CRT}" + echo "AUTH_KEY=${AUTH_KEY}" + echo "GATEWAY_CRT=${GATEWAY_CRT}" + echo "GATEWAY_KEY=${GATEWAY_KEY}" # CORS parameters - echo "ALLOWED_CLIENT_IP=${{ secrets.ALLOWED_CLIENT_IP }}" + echo "ALLOWED_CLIENT_IP=${ALLOWED_CLIENT_IP}" } > "$dir/.env" done From 9adedd315924e9ba4a1a9312052db726ea4202dc Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 3 Mar 2025 14:42:50 -0500 Subject: [PATCH 039/160] added update kubeconfig, removed debugging steps --- .github/workflows/deploy.yaml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index ab1d4631..53701f06 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -22,11 +22,6 @@ jobs: - name: Checkout Repository uses: actions/checkout@v2 - - name: Check File Directory - run: | - echo "Checking file directory..." - ls -la - - name: Install Protocol Buffers Compiler run: | sudo apt-get update @@ -96,15 +91,6 @@ jobs: chmod +x ./generate_env.sh ./generate_env.sh - - name: Print Files in Directories - run: | - echo "Files in backend/gateway:" - ls -la backend/gateway - echo "Files in backend/authentication:" - ls -la backend/authentication - echo "Files in backend/query:" - ls -la backend/query - - name: Generate Code run: | cd backend/common && make gen @@ -119,6 +105,10 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} + - name: Update kubeconfig + run: | + aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION + - name: Login to Amazon ECR run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com From 1ef69399df1bd0fbdf7bbd43dd9109506ecdc3bb Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 4 Mar 2025 14:06:00 -0500 Subject: [PATCH 040/160] added debugging statements --- .github/workflows/deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 53701f06..f9889e22 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -176,6 +176,7 @@ jobs: - name: Deploy to EKS run: | + kubectl version --short kubectl apply -f backend/k8s-manifest.yml kubectl apply -f configmap.yaml From 3779debd0872403b26a563e345569e189041722b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Thu, 6 Mar 2025 16:55:05 -0500 Subject: [PATCH 041/160] feat: added new image + deployment file for direct EC2 deployment --- .github/workflows/EKS_deploy.yaml | 185 ++++++++++++++++++++++++++++++ .github/workflows/deploy.yaml | 70 +++-------- .gitignore | 1 + frontend/package.json | 2 +- 4 files changed, 201 insertions(+), 57 deletions(-) create mode 100644 .github/workflows/EKS_deploy.yaml diff --git a/.github/workflows/EKS_deploy.yaml b/.github/workflows/EKS_deploy.yaml new file mode 100644 index 00000000..c56b144f --- /dev/null +++ b/.github/workflows/EKS_deploy.yaml @@ -0,0 +1,185 @@ +name: Deploy to Amazon EKS +# on: +# push: +# branches: +# - cicd # Trigger deployment on the cicd branch (currently deactivated) + +env: + AWS_REGION: us-east-1 + EKS_CLUSTER_NAME: backend-cluster + ECR_REPOSITORY_NAMESPACE: indeq + IMAGE_TAG: ${{ github.run_id }} + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + id-token: write # For OIDC auth (recommended) + contents: read + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Install Protocol Buffers Compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler + + - name: Install Go + run: | + sudo apt-get update + sudo apt-get install -y golang-go + + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Install protoc-gen-go-grpc + run: | + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Check protoc-gen-go Installation + run: | + protoc-gen-go --version || echo "protoc-gen-go not found" + + - name: Generate .env Files + env: + AUTH_PORT: ${{ secrets.AUTH_PORT }} + AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + QUERY_PORT: ${{ secrets.QUERY_PORT }} + QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + CA_CRT: ${{ secrets.CA_CRT }} + QUERY_CRT: ${{ secrets.QUERY_CRT }} + QUERY_KEY: ${{ secrets.QUERY_KEY }} + AUTH_CRT: ${{ secrets.AUTH_CRT }} + AUTH_KEY: ${{ secrets.AUTH_KEY }} + GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + + run: | + chmod +x ./generate_env.sh + ./generate_env.sh + + - name: Generate Code + run: | + cd backend/common && make gen + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Update kubeconfig + run: | + aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION + + - name: Login to Amazon ECR + run: | + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + + - name: Build and Push Docker Images + run: | + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + + - name: Create config.yaml with GitHub secrets + run: | + echo "apiVersion: v1 + kind: ConfigMap + metadata: + name: backend-config + data: + AUTH_PORT: \"\${{ secrets.AUTH_PORT }}\" + AUTH_ADDRESS: \"\${{ secrets.AUTH_ADDRESS }}\" + QUERY_PORT: \"\${{ secrets.QUERY_PORT }}\" + QUERY_ADDRESS: \"\${{ secrets.QUERY_ADDRESS }}\" + VECTOR_PORT: \"\${{ secrets.VECTOR_PORT }}\" + VECTOR_ADDRESS: \"\${{ secrets.VECTOR_ADDRESS }}\" + GATEWAY_ADDRESS: \"\${{ secrets.GATEWAY_ADDRESS }}\" + DATABASE_URL: \"\${{ secrets.DATABASE_URL }}\" + POSTGRES_USER: \"\${{ secrets.POSTGRES_USER }}\" + POSTGRES_PASSWORD: \"\${{ secrets.POSTGRES_PASSWORD }}\" + POSTGRES_DB: \"\${{ secrets.POSTGRES_DB }}\" + JWT_SECRET: \"\${{ secrets.JWT_SECRET }}\" + ARGON2_MEMORY: \"\${{ secrets.ARGON2_MEMORY }}\" + ARGON2_ITERATIONS: \"\${{ secrets.ARGON2_ITERATIONS }}\" + ARGON2_PARALLELISM: \"\${{ secrets.ARGON2_PARALLELISM }}\" + ARGON2_SALT_LENGTH: \"\${{ secrets.ARGON2_SALT_LENGTH }}\" + ARGON2_KEY_LENGTH: \"\${{ secrets.ARGON2_KEY_LENGTH }}\" + MIN_PASSWORD_LENGTH: \"\${{ secrets.MIN_PASSWORD_LENGTH }}\" + MAX_PASSWORD_LENGTH: \"\${{ secrets.MAX_PASSWORD_LENGTH }}\" + MAX_EMAIL_LENGTH: \"\${{ secrets.MAX_EMAIL_LENGTH }}\" + RABBITMQ_URL: \"\${{ secrets.RABBITMQ_URL }}\" + RABBITMQ_DEFAULT_USER: \"\${{ secrets.RABBITMQ_DEFAULT_USER }}\" + RABBITMQ_DEFAULT_PASS: \"\${{ secrets.RABBITMQ_DEFAULT_PASS }}\" + RABBITMQ_LOGS: \"\${{ secrets.RABBITMQ_LOGS }}\" + OLLAMA_URL: \"\${{ secrets.OLLAMA_URL }}\" + LLM_MODEL: \"\${{ secrets.LLM_MODEL }}\" + ZILLIZ_ADDRESS: \"\${{ secrets.ZILLIZ_ADDRESS }}\" + ZILLIZ_API_KEY: \"\${{ secrets.ZILLIZ_API_KEY }}\" + CA_CRT: \"\${{ secrets.CA_CRT }}\" + QUERY_CRT: \"\${{ secrets.QUERY_CRT }}\" + QUERY_KEY: \"\${{ secrets.QUERY_KEY }}\" + AUTH_CRT: \"\${{ secrets.AUTH_CRT }}\" + AUTH_KEY: \"\${{ secrets.AUTH_KEY }}\" + ALLOWED_CLIENT_IP: \"\${{ secrets.ALLOWED_CLIENT_IP }}\" + " > configmap.yaml + + - name: Configure kubectl + run: | + aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION + + - name: Deploy to EKS + run: | + kubectl version --short + kubectl apply -f backend/k8s-manifest.yml + kubectl apply -f configmap.yaml + + - name: Debug Environment Variables + run: | + echo "Current GOPATH: $(go env GOPATH)" + echo "Current PATH: $PATH" diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index f9889e22..a66311f5 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -127,60 +127,18 @@ jobs: docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - - name: Create config.yaml with GitHub secrets - run: | - echo "apiVersion: v1 - kind: ConfigMap - metadata: - name: backend-config - data: - AUTH_PORT: \"\${{ secrets.AUTH_PORT }}\" - AUTH_ADDRESS: \"\${{ secrets.AUTH_ADDRESS }}\" - QUERY_PORT: \"\${{ secrets.QUERY_PORT }}\" - QUERY_ADDRESS: \"\${{ secrets.QUERY_ADDRESS }}\" - VECTOR_PORT: \"\${{ secrets.VECTOR_PORT }}\" - VECTOR_ADDRESS: \"\${{ secrets.VECTOR_ADDRESS }}\" - GATEWAY_ADDRESS: \"\${{ secrets.GATEWAY_ADDRESS }}\" - DATABASE_URL: \"\${{ secrets.DATABASE_URL }}\" - POSTGRES_USER: \"\${{ secrets.POSTGRES_USER }}\" - POSTGRES_PASSWORD: \"\${{ secrets.POSTGRES_PASSWORD }}\" - POSTGRES_DB: \"\${{ secrets.POSTGRES_DB }}\" - JWT_SECRET: \"\${{ secrets.JWT_SECRET }}\" - ARGON2_MEMORY: \"\${{ secrets.ARGON2_MEMORY }}\" - ARGON2_ITERATIONS: \"\${{ secrets.ARGON2_ITERATIONS }}\" - ARGON2_PARALLELISM: \"\${{ secrets.ARGON2_PARALLELISM }}\" - ARGON2_SALT_LENGTH: \"\${{ secrets.ARGON2_SALT_LENGTH }}\" - ARGON2_KEY_LENGTH: \"\${{ secrets.ARGON2_KEY_LENGTH }}\" - MIN_PASSWORD_LENGTH: \"\${{ secrets.MIN_PASSWORD_LENGTH }}\" - MAX_PASSWORD_LENGTH: \"\${{ secrets.MAX_PASSWORD_LENGTH }}\" - MAX_EMAIL_LENGTH: \"\${{ secrets.MAX_EMAIL_LENGTH }}\" - RABBITMQ_URL: \"\${{ secrets.RABBITMQ_URL }}\" - RABBITMQ_DEFAULT_USER: \"\${{ secrets.RABBITMQ_DEFAULT_USER }}\" - RABBITMQ_DEFAULT_PASS: \"\${{ secrets.RABBITMQ_DEFAULT_PASS }}\" - RABBITMQ_LOGS: \"\${{ secrets.RABBITMQ_LOGS }}\" - OLLAMA_URL: \"\${{ secrets.OLLAMA_URL }}\" - LLM_MODEL: \"\${{ secrets.LLM_MODEL }}\" - ZILLIZ_ADDRESS: \"\${{ secrets.ZILLIZ_ADDRESS }}\" - ZILLIZ_API_KEY: \"\${{ secrets.ZILLIZ_API_KEY }}\" - CA_CRT: \"\${{ secrets.CA_CRT }}\" - QUERY_CRT: \"\${{ secrets.QUERY_CRT }}\" - QUERY_KEY: \"\${{ secrets.QUERY_KEY }}\" - AUTH_CRT: \"\${{ secrets.AUTH_CRT }}\" - AUTH_KEY: \"\${{ secrets.AUTH_KEY }}\" - ALLOWED_CLIENT_IP: \"\${{ secrets.ALLOWED_CLIENT_IP }}\" - " > configmap.yaml - - - name: Configure kubectl - run: | - aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION + docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend + docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + # - name: Deploy to EC2 + # run: | + # ssh -o StrictHostKeyChecking=no -i private_key.pem ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF' + # aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com + # cd /home/${{ secrets.EC2_USER }}/app + # git pull origin main + # docker-compose down + # docker-compose pull + # docker-compose up -d --remove-orphans + # docker system prune -f + # EOF - - name: Deploy to EKS - run: | - kubectl version --short - kubectl apply -f backend/k8s-manifest.yml - kubectl apply -f configmap.yaml - - - name: Debug Environment Variables - run: | - echo "Current GOPATH: $(go env GOPATH)" - echo "Current PATH: $PATH" diff --git a/.gitignore b/.gitignore index 7ee87daf..d949a8f9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ .env .env.local backend-config.yaml +root_ca.crt # Log files *.log diff --git a/frontend/package.json b/frontend/package.json index e1ff0e5f..f531af1f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "vite": "^6.0.0" }, "dependencies": { - "mongodb": "^6.14.0", + "mongodb": "^6.14.2", "svelte-feather-icons": "^4.2.0" } } From 2dd6db8d99e381a909c00999fcd5c65aa7e55903 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Thu, 6 Mar 2025 17:05:19 -0500 Subject: [PATCH 042/160] feat: removed references to EKS cluster --- .github/workflows/deploy.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index a66311f5..f09b2735 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -7,7 +7,6 @@ on: env: AWS_REGION: us-east-1 - EKS_CLUSTER_NAME: backend-cluster ECR_REPOSITORY_NAMESPACE: indeq IMAGE_TAG: ${{ github.run_id }} @@ -105,10 +104,6 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - - name: Update kubeconfig - run: | - aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION - - name: Login to Amazon ECR run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com From 8d45a5d3400cc3de14da70673c48611909641acd Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 00:58:17 -0500 Subject: [PATCH 043/160] feat: added deployment --- .github/workflows/deploy.yaml | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index f09b2735..0cfed9c5 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -125,15 +125,28 @@ jobs: docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - # - name: Deploy to EC2 - # run: | - # ssh -o StrictHostKeyChecking=no -i private_key.pem ${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }} << 'EOF' - # aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com - # cd /home/${{ secrets.EC2_USER }}/app - # git pull origin main - # docker-compose down - # docker-compose pull - # docker-compose up -d --remove-orphans - # docker system prune -f - # EOF + + - name: Deploy to EC2 + run: | + # Install AWS CLI if not already installed + if ! command -v aws &> /dev/null; then + echo "AWS CLI not found. Installing..." + sudo apt-get update + sudo apt-get install -y awscli + fi + + # Copy the Docker image to the EC2 instance + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + + # SSH into the EC2 instance and run the Docker containers + ssh -o StrictHostKeyChecking=no -i ${{ secrets.EC2_KEY_PAIR }} ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + EOF From c0e59ce9135865e3a20aec368f2f2398a4b19dda Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 01:11:27 -0500 Subject: [PATCH 044/160] fix: added step to create .pem file --- .github/workflows/deploy.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 0cfed9c5..d5796b93 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -128,6 +128,7 @@ jobs: - name: Deploy to EC2 run: | + echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then echo "AWS CLI not found. Installing..." @@ -143,7 +144,7 @@ jobs: docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i ${{ secrets.EC2_KEY_PAIR }} ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG From 5b07529060ba266038ee40b37800c4f444d2b839 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 01:15:24 -0500 Subject: [PATCH 045/160] fix: added chmod --- .github/workflows/deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index d5796b93..efd8f4a4 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -129,6 +129,7 @@ jobs: - name: Deploy to EC2 run: | echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem + chmod 600 private_key.pem # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then echo "AWS CLI not found. Installing..." From 21a7c18ec78ee7a1905197873fe03d6d603c66f7 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 15:11:13 -0500 Subject: [PATCH 046/160] test: testing ssh --- .github/workflows/deploy copy.yaml | 154 +++++++++++++++++++++++++++++ .github/workflows/deploy.yaml | 8 +- 2 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/deploy copy.yaml diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml new file mode 100644 index 00000000..86194a7c --- /dev/null +++ b/.github/workflows/deploy copy.yaml @@ -0,0 +1,154 @@ +name: Deploy to Amazon EKS + +on: + push: + branches: + - cicd # Trigger deployment on the cicd branch + +env: + AWS_REGION: us-east-1 + ECR_REPOSITORY_NAMESPACE: indeq + IMAGE_TAG: ${{ github.run_id }} + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + id-token: write # For OIDC auth (recommended) + contents: read + + steps: + # - name: Checkout Repository + # uses: actions/checkout@v2 + + # - name: Install Protocol Buffers Compiler + # run: | + # sudo apt-get update + # sudo apt-get install -y protobuf-compiler + + # - name: Install Go + # run: | + # sudo apt-get update + # sudo apt-get install -y golang-go + + # - name: Install protoc-gen-go + # run: | + # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Install protoc-gen-go-grpc + # run: | + # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Check protoc-gen-go Installation + # run: | + # protoc-gen-go --version || echo "protoc-gen-go not found" + + # - name: Generate .env Files + # env: + # AUTH_PORT: ${{ secrets.AUTH_PORT }} + # AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + # QUERY_PORT: ${{ secrets.QUERY_PORT }} + # QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + # VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + # VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + # GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + # DATABASE_URL: ${{ secrets.DATABASE_URL }} + # POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + # POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + # POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + # JWT_SECRET: ${{ secrets.JWT_SECRET }} + # ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + # ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + # ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + # ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + # ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + # MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + # MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + # MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + # RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + # RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + # RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + # RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + # OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + # LLM_MODEL: ${{ secrets.LLM_MODEL }} + # ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + # ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + # CA_CRT: ${{ secrets.CA_CRT }} + # QUERY_CRT: ${{ secrets.QUERY_CRT }} + # QUERY_KEY: ${{ secrets.QUERY_KEY }} + # AUTH_CRT: ${{ secrets.AUTH_CRT }} + # AUTH_KEY: ${{ secrets.AUTH_KEY }} + # GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + # GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + # ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + + # run: | + # chmod +x ./generate_env.sh + # ./generate_env.sh + + # - name: Generate Code + # run: | + # cd backend/common && make gen + + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v1 + + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v2 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: ${{ env.AWS_REGION }} + + # - name: Login to Amazon ECR + # run: | + # aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + + # - name: Build and Push Docker Images + # run: | + # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + # docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + + # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + # docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + + # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + # docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + + # docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend + # docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + + - name: Deploy to EC2 + run: | + echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem + chmod 600 private_key.pem + # Install AWS CLI if not already installed + if ! command -v aws &> /dev/null; then + echo "AWS CLI not found. Installing..." + sudo apt-get update + sudo apt-get install -y awscli + fi + + # Copy the Docker image to the EC2 instance + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + + # SSH into the EC2 instance and run the Docker containers + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 + docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 + docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 + docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + EOF + diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index efd8f4a4..f32cb0ef 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -1,9 +1,9 @@ name: Deploy to Amazon EKS -on: - push: - branches: - - cicd # Trigger deployment on the cicd branch +# on: +# push: +# branches: +# - cicd # Trigger deployment on the cicd branch env: AWS_REGION: us-east-1 From 8367032ffd071e95ceeb08872b4ff7677b3f2848 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 15:15:09 -0500 Subject: [PATCH 047/160] test: added aws login credentials --- .github/workflows/deploy copy.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 86194a7c..a25b3a09 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -97,16 +97,16 @@ jobs: # - name: Set up Docker Buildx # uses: docker/setup-buildx-action@v1 - # - name: Configure AWS Credentials - # uses: aws-actions/configure-aws-credentials@v2 - # with: - # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - # aws-region: ${{ env.AWS_REGION }} - - # - name: Login to Amazon ECR - # run: | - # aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + run: | + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com # - name: Build and Push Docker Images # run: | From bf29a3e31d18118ffbeb4a550f31593da916e0d8 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 17:14:20 -0500 Subject: [PATCH 048/160] feat: trying appleboy --- .github/workflows/deploy copy.yaml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index a25b3a09..051ffe0b 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -125,7 +125,30 @@ jobs: # docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend # docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - + - name: SSH into EC2 and Deploy + uses: appleboy/ssh-action@v0.1.10 + with: + host: ${{ secrets.EC2_PUBLIC_HOST }} + username: ec2-user + key: ${{ secrets.EC2_KEY_PAIR }} + script: | + echo "Connected to EC2" + # Copy the Docker image to the EC2 instance + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + + # SSH into the EC2 instance and run the Docker containers + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 + docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 + docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 + docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + EOF + + - name: Deploy to EC2 run: | echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem From 0e67dfac5f9b469262f02231e1a6b474c070e5f4 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 17:35:47 -0500 Subject: [PATCH 049/160] feat: added setsecrets.ps1 instruction to readme, readded pem keys w/o whitespace --- CONTRIBUTING.MD | 2 +- backend/setsecrets.ps1 | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD index 545c6358..7068b5fe 100644 --- a/CONTRIBUTING.MD +++ b/CONTRIBUTING.MD @@ -51,7 +51,7 @@ Here you will find all the necessary information for developers to install and s `docker exec -it bash` while your container is running - For postgresql containers, `psql -U ` will open the postgres terminal - If you are running into CORS issues, make sure you have your CA certificate set up correctly and add your frontend IP to the .env file - +- If need to inject env variables, add them to backend-config.yaml and run ./setsecrets.ps1 in backend dir ### Frontend diff --git a/backend/setsecrets.ps1 b/backend/setsecrets.ps1 index fd15448d..5ea606f7 100644 --- a/backend/setsecrets.ps1 +++ b/backend/setsecrets.ps1 @@ -24,7 +24,11 @@ foreach ($secret in $data) { # Extract the key and value $name = $keyValue[0].Trim() # This will be the key - $value = $keyValue[1].Trim() # This will be the value if it exists + if ($name -eq 'EC2_KEY_PAIR' -or $name -eq 'ROOT_CA_CERT') { + $value = $keyValue[1].Trim() -replace '^\s+|\s+$', '' # Trim extra whitespace for specific variables + } else { + $value = $keyValue[1].Trim() # This will be the value if it exists + } # Debug output to check the extracted values $value = $value -replace '^"|"$', '' # Remove extra outer double quotes from the value From e5ec99e4fd8177b865800965024ba1257d33bfdd Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 17:38:40 -0500 Subject: [PATCH 050/160] fix: removed appleboy --- .github/{workflows => }/EKS_deploy.yaml | 0 .github/{workflows => }/deploy.yaml | 0 .github/workflows/deploy copy.yaml | 22 ---------------------- 3 files changed, 22 deletions(-) rename .github/{workflows => }/EKS_deploy.yaml (100%) rename .github/{workflows => }/deploy.yaml (100%) diff --git a/.github/workflows/EKS_deploy.yaml b/.github/EKS_deploy.yaml similarity index 100% rename from .github/workflows/EKS_deploy.yaml rename to .github/EKS_deploy.yaml diff --git a/.github/workflows/deploy.yaml b/.github/deploy.yaml similarity index 100% rename from .github/workflows/deploy.yaml rename to .github/deploy.yaml diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 051ffe0b..9a54b7e7 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -125,28 +125,6 @@ jobs: # docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend # docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - - name: SSH into EC2 and Deploy - uses: appleboy/ssh-action@v0.1.10 - with: - host: ${{ secrets.EC2_PUBLIC_HOST }} - username: ec2-user - key: ${{ secrets.EC2_KEY_PAIR }} - script: | - echo "Connected to EC2" - # Copy the Docker image to the EC2 instance - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 - - # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' - docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 - docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 - docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 - docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 - EOF - name: Deploy to EC2 From 6bf5020220a41068c5b2d78586c7afc7e188e79a Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 17:43:46 -0500 Subject: [PATCH 051/160] fix: added debugging --- .github/workflows/deploy copy.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 9a54b7e7..41e92652 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -145,8 +145,10 @@ jobs: docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + echo "Content of the PEM file:" + cat private_key.pem # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o -v StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 From 0e87f037e9a3b58053f6e40d43aa68bd6912c6ba Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 17:45:10 -0500 Subject: [PATCH 052/160] fix: syntax --- .github/workflows/deploy copy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 41e92652..72a7acff 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -148,7 +148,7 @@ jobs: echo "Content of the PEM file:" cat private_key.pem # SSH into the EC2 instance and run the Docker containers - ssh -o -v StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 From f139349d6bcafa171c0550e20cd20b0ccc579a79 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 17:52:16 -0500 Subject: [PATCH 053/160] fix: added line to return \r --- .github/workflows/deploy copy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 72a7acff..c84c9134 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -129,7 +129,7 @@ jobs: - name: Deploy to EC2 run: | - echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem + echo "${{ secrets.SSH_KEY }}" | tr -d '\r' > private_key.pem chmod 600 private_key.pem # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then From 0f1afc9df62c7cabd3f0d916a81c3c2d76e7bd53 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 17:56:42 -0500 Subject: [PATCH 054/160] fix: changed var name --- .github/workflows/deploy copy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index c84c9134..0496a2d0 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -129,7 +129,7 @@ jobs: - name: Deploy to EC2 run: | - echo "${{ secrets.SSH_KEY }}" | tr -d '\r' > private_key.pem + echo "${{ secrets.EC2_KEY_PAIR }}" | tr -d '\r' > private_key.pem chmod 600 private_key.pem # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then From 2f4be085df2337e626450bede47489fe2fc84120 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 18:02:26 -0500 Subject: [PATCH 055/160] fix: debugging --- .github/workflows/deploy copy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 0496a2d0..6dc1b83b 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -131,6 +131,7 @@ jobs: run: | echo "${{ secrets.EC2_KEY_PAIR }}" | tr -d '\r' > private_key.pem chmod 600 private_key.pem + cat private_key.pem # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then echo "AWS CLI not found. Installing..." From a53468f0be2a8b869b4a83cc10eebe1b8ba3ba76 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 18:13:15 -0500 Subject: [PATCH 056/160] fix: tried removing quotes --- .github/workflows/deploy copy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 6dc1b83b..79fccc68 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -129,9 +129,9 @@ jobs: - name: Deploy to EC2 run: | - echo "${{ secrets.EC2_KEY_PAIR }}" | tr -d '\r' > private_key.pem + echo ${{ secrets.EC2_KEY_PAIR }} | tr -d '\r' > private_key.pem chmod 600 private_key.pem - cat private_key.pem + # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then echo "AWS CLI not found. Installing..." @@ -147,7 +147,7 @@ jobs: docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 echo "Content of the PEM file:" - cat private_key.pem + # SSH into the EC2 instance and run the Docker containers ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 From 9b9e7c71ac9d62894372aa6c7bf647a7af0c187e Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Fri, 7 Mar 2025 18:14:36 -0500 Subject: [PATCH 057/160] fix: check file length --- .github/workflows/deploy copy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 79fccc68..910f230d 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -131,7 +131,7 @@ jobs: run: | echo ${{ secrets.EC2_KEY_PAIR }} | tr -d '\r' > private_key.pem chmod 600 private_key.pem - + echo "length of private key: $(wc -c < private_key.pem)" # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then echo "AWS CLI not found. Installing..." From cfa0c72bf77bb8dca47016749d7d06e2f79fdd1f Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 11:48:03 -0500 Subject: [PATCH 058/160] fix: added clean command for pem key for libcrypto --- .github/workflows/deploy copy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 910f230d..fd03d218 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -129,7 +129,9 @@ jobs: - name: Deploy to EC2 run: | + echo ${{ secrets.EC2_KEY_PAIR }} | tr -d '\r' > private_key.pem + vim --clean private_key.pem chmod 600 private_key.pem echo "length of private key: $(wc -c < private_key.pem)" # Install AWS CLI if not already installed From 4fb0c9d5fde671cab3430b53227deb8f77e1c408 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 12:17:43 -0500 Subject: [PATCH 059/160] fix: injected a cleaned pem file format version --- .github/workflows/deploy copy.yaml | 5 ++++- backend/setsecrets.ps1 | 21 ++++++++++++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index fd03d218..cd5b04e9 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -130,7 +130,7 @@ jobs: - name: Deploy to EC2 run: | - echo ${{ secrets.EC2_KEY_PAIR }} | tr -d '\r' > private_key.pem + echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem vim --clean private_key.pem chmod 600 private_key.pem echo "length of private key: $(wc -c < private_key.pem)" @@ -158,3 +158,6 @@ jobs: docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 EOF + # Create a temporary file to hold the formatted key + cat private_key.pem | tr '\n' '|' | sed 's/|/\\n/g' > formatted_key.txt + diff --git a/backend/setsecrets.ps1 b/backend/setsecrets.ps1 index 5ea606f7..93d4e7df 100644 --- a/backend/setsecrets.ps1 +++ b/backend/setsecrets.ps1 @@ -24,12 +24,22 @@ foreach ($secret in $data) { # Extract the key and value $name = $keyValue[0].Trim() # This will be the key - if ($name -eq 'EC2_KEY_PAIR' -or $name -eq 'ROOT_CA_CERT') { - $value = $keyValue[1].Trim() -replace '^\s+|\s+$', '' # Trim extra whitespace for specific variables - } else { - $value = $keyValue[1].Trim() # This will be the value if it exists + $value = $keyValue[1].Trim() # This will be the value if it exists + + # For EC2_KEY_PAIR, ensure proper line endings + if ($name -eq 'EC2_KEY_PAIR') { + # Read the key file + $keyContent = $value + + # Split the key into parts + $beginMarker = "-----BEGIN RSA PRIVATE KEY-----" + $endMarker = "-----END RSA PRIVATE KEY-----" + $keyBody = $keyContent -replace $beginMarker, "" -replace $endMarker, "" -replace "\s+", "" + + + # Reconstruct the key with proper format + $value = $beginMarker + $formattedKey + $endMarker } - # Debug output to check the extracted values $value = $value -replace '^"|"$', '' # Remove extra outer double quotes from the value Write-Host "Extracted secret: Name='$name', Value='$value'" @@ -39,6 +49,7 @@ foreach ($secret in $data) { continue } + # Set the secret in GitHub gh secret set $name --body $value From 45e763f3aed8e0142fef9d4b3a7228e77eb09e33 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 12:18:47 -0500 Subject: [PATCH 060/160] fix: syntax error --- .github/workflows/deploy copy.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index cd5b04e9..a6fbc04d 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -158,6 +158,4 @@ jobs: docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 EOF - # Create a temporary file to hold the formatted key - cat private_key.pem | tr '\n' '|' | sed 's/|/\\n/g' > formatted_key.txt From 1de797e080afe745542d5d4e7c2e08fe2f951d6b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 12:27:30 -0500 Subject: [PATCH 061/160] fix: syntax --- .github/workflows/deploy copy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index a6fbc04d..58fb280f 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -131,7 +131,6 @@ jobs: run: | echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem - vim --clean private_key.pem chmod 600 private_key.pem echo "length of private key: $(wc -c < private_key.pem)" # Install AWS CLI if not already installed From 060c80a918a788d44f9ff3e0a192d584c1004b6e Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 12:32:10 -0500 Subject: [PATCH 062/160] fix: syntax --- .github/workflows/deploy copy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 58fb280f..9b4dc318 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -157,4 +157,3 @@ jobs: docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 EOF - From 37d2dc340a1f7a9800503836002cad9012f56bbb Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 12:37:57 -0500 Subject: [PATCH 063/160] feat: only push key contents to secret, reconstruct file correctly in git bash cicd script --- .github/workflows/deploy copy.yaml | 11 +++++++++-- backend/setsecrets.ps1 | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 9b4dc318..72d0268e 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -130,9 +130,16 @@ jobs: - name: Deploy to EC2 run: | - echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem + # Extract markers + BEGIN_MARKER="-----BEGIN RSA PRIVATE KEY-----" + END_MARKER="-----END RSA PRIVATE KEY-----" + + # Create PEM file with proper formatting + echo "$BEGIN_MARKER" > private_key.pem + echo "${{ secrets.EC2_KEY_PAIR }}" | sed 's/.\{64\}/&\n/g' >> private_key.pem + echo "$END_MARKER" >> private_key.pem + chmod 600 private_key.pem - echo "length of private key: $(wc -c < private_key.pem)" # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then echo "AWS CLI not found. Installing..." diff --git a/backend/setsecrets.ps1 b/backend/setsecrets.ps1 index 93d4e7df..edb2277e 100644 --- a/backend/setsecrets.ps1 +++ b/backend/setsecrets.ps1 @@ -38,7 +38,7 @@ foreach ($secret in $data) { # Reconstruct the key with proper format - $value = $beginMarker + $formattedKey + $endMarker + $value = $keyBody } # Debug output to check the extracted values $value = $value -replace '^"|"$', '' # Remove extra outer double quotes from the value From 166a4b73a00ea424ca5f1a7577bbc32ffee8faf8 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 12:45:15 -0500 Subject: [PATCH 064/160] fix: reverted file back, can't trigger pipeline run --- .github/deploy.yaml | 218 ++++++++++++++++++++++---------------------- 1 file changed, 111 insertions(+), 107 deletions(-) diff --git a/.github/deploy.yaml b/.github/deploy.yaml index f32cb0ef..910f230d 100644 --- a/.github/deploy.yaml +++ b/.github/deploy.yaml @@ -1,9 +1,9 @@ name: Deploy to Amazon EKS -# on: -# push: -# branches: -# - cicd # Trigger deployment on the cicd branch +on: + push: + branches: + - cicd # Trigger deployment on the cicd branch env: AWS_REGION: us-east-1 @@ -18,84 +18,84 @@ jobs: contents: read steps: - - name: Checkout Repository - uses: actions/checkout@v2 - - - name: Install Protocol Buffers Compiler - run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler - - - name: Install Go - run: | - sudo apt-get update - sudo apt-get install -y golang-go - - - name: Install protoc-gen-go - run: | - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Install protoc-gen-go-grpc - run: | - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Check protoc-gen-go Installation - run: | - protoc-gen-go --version || echo "protoc-gen-go not found" - - - name: Generate .env Files - env: - AUTH_PORT: ${{ secrets.AUTH_PORT }} - AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} - QUERY_PORT: ${{ secrets.QUERY_PORT }} - QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} - VECTOR_PORT: ${{ secrets.VECTOR_PORT }} - VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} - GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - JWT_SECRET: ${{ secrets.JWT_SECRET }} - ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} - ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} - ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} - ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} - ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} - MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} - MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} - MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} - RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} - RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} - RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} - RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} - OLLAMA_URL: ${{ secrets.OLLAMA_URL }} - LLM_MODEL: ${{ secrets.LLM_MODEL }} - ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} - ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} - CA_CRT: ${{ secrets.CA_CRT }} - QUERY_CRT: ${{ secrets.QUERY_CRT }} - QUERY_KEY: ${{ secrets.QUERY_KEY }} - AUTH_CRT: ${{ secrets.AUTH_CRT }} - AUTH_KEY: ${{ secrets.AUTH_KEY }} - GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} - GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} - ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + # - name: Checkout Repository + # uses: actions/checkout@v2 + + # - name: Install Protocol Buffers Compiler + # run: | + # sudo apt-get update + # sudo apt-get install -y protobuf-compiler + + # - name: Install Go + # run: | + # sudo apt-get update + # sudo apt-get install -y golang-go + + # - name: Install protoc-gen-go + # run: | + # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Install protoc-gen-go-grpc + # run: | + # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Check protoc-gen-go Installation + # run: | + # protoc-gen-go --version || echo "protoc-gen-go not found" + + # - name: Generate .env Files + # env: + # AUTH_PORT: ${{ secrets.AUTH_PORT }} + # AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + # QUERY_PORT: ${{ secrets.QUERY_PORT }} + # QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + # VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + # VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + # GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + # DATABASE_URL: ${{ secrets.DATABASE_URL }} + # POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + # POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + # POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + # JWT_SECRET: ${{ secrets.JWT_SECRET }} + # ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + # ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + # ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + # ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + # ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + # MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + # MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + # MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + # RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + # RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + # RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + # RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + # OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + # LLM_MODEL: ${{ secrets.LLM_MODEL }} + # ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + # ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + # CA_CRT: ${{ secrets.CA_CRT }} + # QUERY_CRT: ${{ secrets.QUERY_CRT }} + # QUERY_KEY: ${{ secrets.QUERY_KEY }} + # AUTH_CRT: ${{ secrets.AUTH_CRT }} + # AUTH_KEY: ${{ secrets.AUTH_KEY }} + # GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + # GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + # ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} - run: | - chmod +x ./generate_env.sh - ./generate_env.sh + # run: | + # chmod +x ./generate_env.sh + # ./generate_env.sh - - name: Generate Code - run: | - cd backend/common && make gen + # - name: Generate Code + # run: | + # cd backend/common && make gen - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v1 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 @@ -108,28 +108,30 @@ jobs: run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - - name: Build and Push Docker Images - run: | - docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - - docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - - docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - - docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend - docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - + # - name: Build and Push Docker Images + # run: | + # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + # docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + + # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + # docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + + # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + # docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + + # docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend + # docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + + - name: Deploy to EC2 run: | - echo ${{ secrets.EC2_KEY_PAIR }} > private_key.pem + echo ${{ secrets.EC2_KEY_PAIR }} | tr -d '\r' > private_key.pem chmod 600 private_key.pem + echo "length of private key: $(wc -c < private_key.pem)" # Install AWS CLI if not already installed if ! command -v aws &> /dev/null; then echo "AWS CLI not found. Installing..." @@ -139,16 +141,18 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + echo "Content of the PEM file:" + # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' - docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 + docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 + docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 + docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 EOF From 5e3d1927a6ab878a331729c081214db2617fcd04 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 13:04:47 -0500 Subject: [PATCH 065/160] fix: testing cicd --- .github/workflows/deploy copy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 72d0268e..218fe9b8 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -129,7 +129,7 @@ jobs: - name: Deploy to EC2 run: | - + # Extract markers BEGIN_MARKER="-----BEGIN RSA PRIVATE KEY-----" END_MARKER="-----END RSA PRIVATE KEY-----" From 37f6c770a660801f88311c9f293d7a39d21e5100 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 13:06:36 -0500 Subject: [PATCH 066/160] fix: syntax --- .github/workflows/deploy copy.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 218fe9b8..13334fd0 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -126,10 +126,8 @@ jobs: # docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - - name: Deploy to EC2 run: | - # Extract markers BEGIN_MARKER="-----BEGIN RSA PRIVATE KEY-----" END_MARKER="-----END RSA PRIVATE KEY-----" From fae25a9634d235e85b2d1498fb020b035aa8804b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 13:13:21 -0500 Subject: [PATCH 067/160] feat: added step to install docker --- .github/workflows/deploy copy.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 13334fd0..7459750a 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -156,6 +156,20 @@ jobs: # SSH into the EC2 instance and run the Docker containers ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + # Install Docker if not already installed + if ! command -v docker &> /dev/null; then + echo "Docker not found. Installing..." + sudo yum update -y + sudo yum install docker -y + sudo systemctl start docker + sudo systemctl enable docker + sudo usermod -a -G docker ec2-user + echo "Docker installed successfully" + fi + + # Verify Docker installation + docker --version + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 From 329624384fbb08a67e1a2f43f3fcd3b4be2231d4 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 13:19:41 -0500 Subject: [PATCH 068/160] feat: removed quotes from EOF and added lines to rm old containers --- .github/workflows/deploy copy.yaml | 6 +- backend/docker-compose-ec2.yaml | 109 +++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 backend/docker-compose-ec2.yaml diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 7459750a..c1e09ef2 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -155,7 +155,7 @@ jobs: echo "Content of the PEM file:" # SSH into the EC2 instance and run the Docker containers - ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << EOF # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." @@ -170,6 +170,10 @@ jobs: # Verify Docker installation docker --version + # Stop and remove all running containers + docker stop $(docker ps -a -q) || true + docker rm $(docker ps -a -q) || true + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml new file mode 100644 index 00000000..ea168c4b --- /dev/null +++ b/backend/docker-compose-ec2.yaml @@ -0,0 +1,109 @@ +services: + query: + image: query-service + build: + context: . + dockerfile: query/Dockerfile + depends_on: + rabbitmq: + condition: service_healthy + networks: + - indeq-net + + # this docker setup doesn't utilize the GPU + ollama: + image: ollama/ollama:latest + ports: + - "11434:11434" + volumes: + - ollama_models:/root/.ollama + environment: + - OLLAMA_NUM_PARALLEL=4 # Number of threads to utilize + mem_limit: 16g + networks: + - indeq-net + + rabbitmq: + image: rabbitmq:3-management + container_name: rabbitmq + env_file: + - ./common/config/.env + ports: + - 5672:5672 # AMQP protocol port + - 15672:15672 # Management UI port + volumes: + - rabbitmq_data:/var/lib/rabbitmq + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"] + interval: 5s + timeout: 5s + retries: 5 + networks: + - indeq-net + + authentication: + image: authentication-service + build: + context: . + dockerfile: authentication/Dockerfile + env_file: + - ./common/config/.env + depends_on: + appDB: + condition: service_healthy + networks: + - indeq-net + + gateway: + image: gateway-service + build: + context: . + dockerfile: gateway/Dockerfile + ports: + - "8080:8080" + depends_on: + query: + condition: service_started + rabbitmq: + condition: service_healthy + networks: + - indeq-net + waitlist: + image: waitlist-service + build: + context: . + dockerfile: waitlist/Dockerfile + env_file: + - ./common/config/.env + depends_on: + appDB: + condition: service_healthy + networks: + - indeq-net + appDB: + image: postgres:latest + env_file: + - ./common/config/.env + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] + interval: 5s + timeout: 5s + retries: 5 + command: + - -c + - ssl=off + expose: + - "5432" + volumes: + - app_data:/var/lib/postgresql/data + networks: + - indeq-net + +volumes: + app_data: + ollama_models: + rabbitmq_data: + +networks: + indeq-net: + driver: bridge From c460a262f13728b52c43dc81df9024257446d02d Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 13:25:13 -0500 Subject: [PATCH 069/160] feat: added image pull step --- .github/workflows/deploy copy.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index c1e09ef2..a545c773 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -173,7 +173,19 @@ jobs: # Stop and remove all running containers docker stop $(docker ps -a -q) || true docker rm $(docker ps -a -q) || true - + + # Configure AWS CLI + aws configure set region us-east-1 + + # Test ECR Login + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + + # Try pulling an image + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156 + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 From 517d1829f57f4b947be612e9d518794ae7a433ed Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 13:34:38 -0500 Subject: [PATCH 070/160] feat: added IAM role to container, now retrying pulling ... --- .github/workflows/deploy copy.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index a545c773..1ddb90ed 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -177,9 +177,12 @@ jobs: # Configure AWS CLI aws configure set region us-east-1 - # Test ECR Login - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + # Verify IAM role + aws sts get-caller-identity + # Login to ECR without credentials (using IAM role) + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + # Try pulling an image docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156 docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156 From 8351a971eeedde785afa5cf741aebcd2c293aded Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:05:25 -0500 Subject: [PATCH 071/160] feat: changed pipeline name, add steps to scp docker-compose file --- .github/workflows/deploy copy.yaml | 49 +++++++++++++++++++----------- backend/docker-compose-ec2.yaml | 20 +++--------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 1ddb90ed..afdda02a 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -1,4 +1,4 @@ -name: Deploy to Amazon EKS +name: Deploy to Amazon EC2 on: push: @@ -147,12 +147,31 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + $authImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156 + $queryImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156 + $gatewayImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156 + $waitlistImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156 + + docker pull $authImage + docker pull $queryImage + docker pull $gatewayImage + docker pull $waitlistImage + docker pull ollama/ollama:latest + docker pull rabbitmq:3-management + docker pull postgres:latest + + # Replace placeholder image references with actual ECR URLs + sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml + sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml + sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml + sed -i "s|waitlistImage|$waitlistImage|g" backend/docker-compose-ec2.yaml - echo "Content of the PEM file:" + # check if the docker-compose-ec2.yaml file has been modified + echo "Content of the docker-compose-ec2.yaml file:" + cat backend/docker-compose-ec2.yaml + + # Copy the docker-compose file to EC2 + scp -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:~/docker-compose.yaml # SSH into the EC2 instance and run the Docker containers ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << EOF @@ -171,8 +190,9 @@ jobs: docker --version # Stop and remove all running containers - docker stop $(docker ps -a -q) || true - docker rm $(docker ps -a -q) || true + echo "Cleaning up all existing containers..." + docker stop \$(docker ps -aq) || true + docker rm \$(docker ps -aq) || true # Configure AWS CLI aws configure set region us-east-1 @@ -183,15 +203,8 @@ jobs: # Login to ECR without credentials (using IAM role) aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - # Try pulling an image - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156 - - docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 - docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 - docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 - docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + # Try running docker compose + docker compose -f docker-compose-ec2.yaml up -d + EOF diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml index ea168c4b..8908e4cf 100644 --- a/backend/docker-compose-ec2.yaml +++ b/backend/docker-compose-ec2.yaml @@ -1,9 +1,6 @@ services: query: - image: query-service - build: - context: . - dockerfile: query/Dockerfile + image: queryImage depends_on: rabbitmq: condition: service_healthy @@ -42,10 +39,7 @@ services: - indeq-net authentication: - image: authentication-service - build: - context: . - dockerfile: authentication/Dockerfile + image: authImage env_file: - ./common/config/.env depends_on: @@ -55,10 +49,7 @@ services: - indeq-net gateway: - image: gateway-service - build: - context: . - dockerfile: gateway/Dockerfile + image: gatewayImage ports: - "8080:8080" depends_on: @@ -69,10 +60,7 @@ services: networks: - indeq-net waitlist: - image: waitlist-service - build: - context: . - dockerfile: waitlist/Dockerfile + image: waitlistImage env_file: - ./common/config/.env depends_on: From fd3aa2c564f90691be1ed1724510443e595ec579 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:18:05 -0500 Subject: [PATCH 072/160] fix: syntax --- .github/workflows/deploy copy.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index afdda02a..215bfd1e 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -147,10 +147,10 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - $authImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156 - $queryImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156 - $gatewayImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156 - $waitlistImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156 + authImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156 + queryImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156 + gatewayImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156 + waitlistImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156 docker pull $authImage docker pull $queryImage From af17e43d873122b5a8993cbb1d0a75b66e3c7374 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:19:22 -0500 Subject: [PATCH 073/160] fix: syntax --- .github/workflows/deploy copy.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 215bfd1e..a4e23b56 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -147,10 +147,10 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - authImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156 - queryImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156 - gatewayImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156 - waitlistImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156 + authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156" + queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156" + gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156" + waitlistImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156" docker pull $authImage docker pull $queryImage From b9a072cc3c1dcbec9da91223e55497398dfe426b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:25:42 -0500 Subject: [PATCH 074/160] fix: debugging statements --- .github/workflows/deploy copy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index a4e23b56..67ebb679 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -168,6 +168,9 @@ jobs: # check if the docker-compose-ec2.yaml file has been modified echo "Content of the docker-compose-ec2.yaml file:" + echo "Current directory: $(pwd)" + ls -la + ls -la backend/ cat backend/docker-compose-ec2.yaml # Copy the docker-compose file to EC2 From 79704d7cc9c747013a48e4387e907ef665c84df0 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:28:37 -0500 Subject: [PATCH 075/160] fix: debugging --- .github/workflows/deploy copy.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 67ebb679..f1acc60f 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -18,8 +18,8 @@ jobs: contents: read steps: - # - name: Checkout Repository - # uses: actions/checkout@v2 + - name: Checkout Repository + uses: actions/checkout@v2 # - name: Install Protocol Buffers Compiler # run: | @@ -144,6 +144,11 @@ jobs: sudo apt-get update sudo apt-get install -y awscli fi + + echo "Current directory: $(pwd)" + ls -la + ls -la backend/ + cat backend/docker-compose-ec2.yaml # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com @@ -168,10 +173,6 @@ jobs: # check if the docker-compose-ec2.yaml file has been modified echo "Content of the docker-compose-ec2.yaml file:" - echo "Current directory: $(pwd)" - ls -la - ls -la backend/ - cat backend/docker-compose-ec2.yaml # Copy the docker-compose file to EC2 scp -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:~/docker-compose.yaml From 3ea34c2c574fa6c0c5cc2240070a9aa880f67ea5 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:45:03 -0500 Subject: [PATCH 076/160] fix: added stricthostkeychecking to no and commented out images for faster testing --- .github/workflows/deploy copy.yaml | 33 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index f1acc60f..942f09d7 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -153,17 +153,17 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156" - queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156" - gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156" - waitlistImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156" + # queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156" + # gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156" + # waitlistImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156" docker pull $authImage - docker pull $queryImage - docker pull $gatewayImage - docker pull $waitlistImage - docker pull ollama/ollama:latest - docker pull rabbitmq:3-management - docker pull postgres:latest + # docker pull $queryImage + # docker pull $gatewayImage + # docker pull $waitlistImage + # docker pull ollama/ollama:latest + # docker pull rabbitmq:3-management + # docker pull postgres:latest # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml @@ -171,14 +171,21 @@ jobs: sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml sed -i "s|waitlistImage|$waitlistImage|g" backend/docker-compose-ec2.yaml - # check if the docker-compose-ec2.yaml file has been modified - echo "Content of the docker-compose-ec2.yaml file:" - # Copy the docker-compose file to EC2 - scp -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:~/docker-compose.yaml + scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ # SSH into the EC2 instance and run the Docker containers ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << EOF + + # check curr dir and if the docker-compose-ec2.yaml file has been modified + echo "Current directory: $(pwd)" + if [ -f "docker-compose-ec2.yaml" ]; then + echo "Content of the docker-compose-ec2.yaml file:" + cat docker-compose-ec2.yaml + else + echo "docker-compose-ec2.yaml file does not exist" + fi + # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From e62c4077e65751aaceabe5e68a8985748695264b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:53:46 -0500 Subject: [PATCH 077/160] fix: added docker-compose command correctly --- .github/workflows/deploy copy.yaml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 942f09d7..63b66854 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -153,17 +153,17 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156" - # queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156" - # gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156" - # waitlistImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156" + queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156" + gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156" + waitlistImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156" docker pull $authImage - # docker pull $queryImage - # docker pull $gatewayImage - # docker pull $waitlistImage - # docker pull ollama/ollama:latest - # docker pull rabbitmq:3-management - # docker pull postgres:latest + docker pull $queryImage + docker pull $gatewayImage + docker pull $waitlistImage + docker pull ollama/ollama:latest + docker pull rabbitmq:3-management + docker pull postgres:latest # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml @@ -172,10 +172,10 @@ jobs: sed -i "s|waitlistImage|$waitlistImage|g" backend/docker-compose-ec2.yaml # Copy the docker-compose file to EC2 - scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ + scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ # SSH into the EC2 instance and run the Docker containers - ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << EOF + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << EOF # check curr dir and if the docker-compose-ec2.yaml file has been modified echo "Current directory: $(pwd)" @@ -215,7 +215,7 @@ jobs: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com # Try running docker compose - docker compose -f docker-compose-ec2.yaml up -d + docker compose up -f docker-compose-ec2.yaml -d EOF From 6d8bb0dcc18ce09d0ce4ba8c88ed6ad446fda476 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 14:57:20 -0500 Subject: [PATCH 078/160] fix: debug syntax --- .github/workflows/deploy copy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 63b66854..3a0e350f 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -215,7 +215,7 @@ jobs: aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com # Try running docker compose - docker compose up -f docker-compose-ec2.yaml -d + docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d EOF From 40a52485ac2f64d907de326114e7001a87a373e5 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 15:02:38 -0500 Subject: [PATCH 079/160] feat: added checks for container removal and docker compose installation --- .github/workflows/deploy copy.yaml | 38 +++++++++++------------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 3a0e350f..f018fc79 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -161,9 +161,6 @@ jobs: docker pull $queryImage docker pull $gatewayImage docker pull $waitlistImage - docker pull ollama/ollama:latest - docker pull rabbitmq:3-management - docker pull postgres:latest # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml @@ -175,16 +172,7 @@ jobs: scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << EOF - - # check curr dir and if the docker-compose-ec2.yaml file has been modified - echo "Current directory: $(pwd)" - if [ -f "docker-compose-ec2.yaml" ]; then - echo "Content of the docker-compose-ec2.yaml file:" - cat docker-compose-ec2.yaml - else - echo "docker-compose-ec2.yaml file does not exist" - fi + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then @@ -192,18 +180,20 @@ jobs: sudo yum update -y sudo yum install docker -y sudo systemctl start docker - sudo systemctl enable docker - sudo usermod -a -G docker ec2-user - echo "Docker installed successfully" - fi - # Verify Docker installation - docker --version + # Install Docker Compose V2 if not present + if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then + echo "Installing Docker Compose V2..." + sudo yum install -y docker-compose-plugin + fi - # Stop and remove all running containers - echo "Cleaning up all existing containers..." - docker stop \$(docker ps -aq) || true - docker rm \$(docker ps -aq) || true + # Stop and remove containers safely + if [ "$(docker ps -q)" ]; then + docker stop $(docker ps -q) + fi + if [ "$(docker ps -aq)" ]; then + docker rm $(docker ps -aq) + fi # Configure AWS CLI aws configure set region us-east-1 @@ -214,7 +204,7 @@ jobs: # Login to ECR without credentials (using IAM role) aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - # Try running docker compose + # Run docker compose with absolute path docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d EOF From c7e8aafb82dbabf8ef333a7cb3c7e287deebfd54 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 15:04:28 -0500 Subject: [PATCH 080/160] fix: syntax --- .github/workflows/deploy copy.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index f018fc79..e4346134 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -173,13 +173,16 @@ jobs: # SSH into the EC2 instance and run the Docker containers ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' - # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." sudo yum update -y sudo yum install docker -y sudo systemctl start docker + sudo systemctl enable docker + sudo usermod -a -G docker ec2-user + echo "Docker installed successfully" + fi # Install Docker Compose V2 if not present if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then @@ -206,6 +209,5 @@ jobs: # Run docker compose with absolute path docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d - EOF From 431ddf71fd306c1cb819ce6c800cee94f6dfc966 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 15:07:01 -0500 Subject: [PATCH 081/160] fix: modified docker compose path installation --- .github/workflows/deploy copy.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index e4346134..94aaec87 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -187,7 +187,10 @@ jobs: # Install Docker Compose V2 if not present if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then echo "Installing Docker Compose V2..." - sudo yum install -y docker-compose-plugin + # For Amazon Linux 2023 + sudo mkdir -p /usr/local/lib/docker/cli-plugins + sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose + sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose fi # Stop and remove containers safely @@ -207,7 +210,8 @@ jobs: # Login to ECR without credentials (using IAM role) aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - # Run docker compose with absolute path - docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d + # Run docker compose with absolute path (using new compose command) + cd /home/ec2-user + docker compose up -d EOF From 6711629007b79a1314c92a5a56b0b68f244efb5b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 15:10:23 -0500 Subject: [PATCH 082/160] fix: added -f flag --- .github/workflows/deploy copy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 94aaec87..1caff4f3 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -210,8 +210,8 @@ jobs: # Login to ECR without credentials (using IAM role) aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - # Run docker compose with absolute path (using new compose command) - cd /home/ec2-user - docker compose up -d + # Run docker compose with absolute path + docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d + EOF From 80a9ca99db0f0ee529a3551044d4539922b38b78 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 15:25:28 -0500 Subject: [PATCH 083/160] feat: added .env creation step and scp --- .github/workflows/deploy copy.yaml | 182 ++++++++++++++++++++++------- 1 file changed, 143 insertions(+), 39 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 1caff4f3..9c637d21 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -47,44 +47,147 @@ jobs: # run: | # protoc-gen-go --version || echo "protoc-gen-go not found" - # - name: Generate .env Files - # env: - # AUTH_PORT: ${{ secrets.AUTH_PORT }} - # AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} - # QUERY_PORT: ${{ secrets.QUERY_PORT }} - # QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} - # VECTOR_PORT: ${{ secrets.VECTOR_PORT }} - # VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} - # GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} - # DATABASE_URL: ${{ secrets.DATABASE_URL }} - # POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - # POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - # POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - # JWT_SECRET: ${{ secrets.JWT_SECRET }} - # ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} - # ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} - # ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} - # ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} - # ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} - # MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} - # MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} - # MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} - # RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} - # RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} - # RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} - # RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} - # OLLAMA_URL: ${{ secrets.OLLAMA_URL }} - # LLM_MODEL: ${{ secrets.LLM_MODEL }} - # ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} - # ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} - # CA_CRT: ${{ secrets.CA_CRT }} - # QUERY_CRT: ${{ secrets.QUERY_CRT }} - # QUERY_KEY: ${{ secrets.QUERY_KEY }} - # AUTH_CRT: ${{ secrets.AUTH_CRT }} - # AUTH_KEY: ${{ secrets.AUTH_KEY }} - # GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} - # GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} - # ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + - name: Generate .env Files + env: + DEV_PROD: ${{ secrets.DEV_PROD }} + AUTH_PORT: ${{ secrets.AUTH_PORT }} + AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + QUERY_PORT: ${{ secrets.QUERY_PORT }} + QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} + DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} + DESKTOP_HTTP_PORT: ${{ secrets.DESKTOP_HTTP_PORT }} + DESKTOP_HTTP_ADDRESS: ${{ secrets.DESKTOP_HTTP_ADDRESS }} + GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + KAFKA_BROKER_ADDRESS: ${{ secrets.KAFKA_BROKER_ADDRESS }} + KAFKA_BROKER_ID: ${{ secrets.KAFKA_BROKER_ID }} + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${{ secrets.KAFKA_LISTENER_SECURITY_PROTOCOL_MAP }} + KAFKA_ADVERTISED_LISTENERS: ${{ secrets.KAFKA_ADVERTISED_LISTENERS }} + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${{ secrets.KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR }} + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: ${{ secrets.KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS }} + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_MIN_ISR }} + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR }} + KAFKA_PROCESS_ROLES: ${{ secrets.KAFKA_PROCESS_ROLES }} + KAFKA_NODE_ID: ${{ secrets.KAFKA_NODE_ID }} + KAFKA_CONTROLLER_QUORUM_VOTERS: ${{ secrets.KAFKA_CONTROLLER_QUORUM_VOTERS }} + KAFKA_LISTENERS: ${{ secrets.KAFKA_LISTENERS }} + KAFKA_INTER_BROKER_LISTENER_NAME: ${{ secrets.KAFKA_INTER_BROKER_LISTENER_NAME }} + KAFKA_CONTROLLER_LISTENER_NAMES: ${{ secrets.KAFKA_CONTROLLER_LISTENER_NAMES }} + CLUSTER_ID: ${{ secrets.CLUSTER_ID }} + KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} + KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} + OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + EMBED_URL: ${{ secrets.EMBED_URL }} + EMBED_MODEL: ${{ secrets.EMBED_MODEL }} + ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} + CA_CRT: ${{ secrets.CA_CRT }} + QUERY_CRT: ${{ secrets.QUERY_CRT }} + QUERY_KEY: ${{ secrets.QUERY_KEY }} + AUTH_CRT: ${{ secrets.AUTH_CRT }} + AUTH_KEY: ${{ secrets.AUTH_KEY }} + VECTOR_CRT: ${{ secrets.VECTOR_CRT }} + VECTOR_KEY: ${{ secrets.VECTOR_KEY }} + GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} + DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} + ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} + EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} + EC2_PUBLIC_HOST: ${{ secrets.EC2_PUBLIC_HOST }} + ROOT_CA_CERT: ${{ secrets.ROOT_CA_CERT }} + run: | + echo "DEV_PROD=$DEV_PROD" > .env + echo "AUTH_PORT=$AUTH_PORT" >> .env + echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> .env + echo "QUERY_PORT=$QUERY_PORT" >> .env + echo "QUERY_ADDRESS=$QUERY_ADDRESS" >> .env + echo "VECTOR_PORT=$VECTOR_PORT" >> .env + echo "VECTOR_ADDRESS=$VECTOR_ADDRESS" >> .env + echo "DESKTOP_ADDRESS=$DESKTOP_ADDRESS" >> .env + echo "DESKTOP_PORT=$DESKTOP_PORT" >> .env + echo "DESKTOP_HTTP_PORT=$DESKTOP_HTTP_PORT" >> .env + echo "DESKTOP_HTTP_ADDRESS=$DESKTOP_HTTP_ADDRESS" >> .env + echo "GATEWAY_ADDRESS=$GATEWAY_ADDRESS" >> .env + echo "DATABASE_URL=$DATABASE_URL" >> .env + echo "POSTGRES_USER=$POSTGRES_USER" >> .env + echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env + echo "POSTGRES_DB=$POSTGRES_DB" >> .env + echo "JWT_SECRET=$JWT_SECRET" >> .env + echo "ARGON2_MEMORY=$ARGON2_MEMORY" >> .env + echo "ARGON2_ITERATIONS=$ARGON2_ITERATIONS" >> .env + echo "ARGON2_PARALLELISM=$ARGON2_PARALLELISM" >> .env + echo "ARGON2_SALT_LENGTH=$ARGON2_SALT_LENGTH" >> .env + echo "ARGON2_KEY_LENGTH=$ARGON2_KEY_LENGTH" >> .env + echo "MIN_PASSWORD_LENGTH=$MIN_PASSWORD_LENGTH" >> .env + echo "MAX_PASSWORD_LENGTH=$MAX_PASSWORD_LENGTH" >> .env + echo "MAX_EMAIL_LENGTH=$MAX_EMAIL_LENGTH" >> .env + echo "RABBITMQ_URL=$RABBITMQ_URL" >> .env + echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> .env + echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> .env + echo "RABBITMQ_LOGS=$RABBITMQ_LOGS" >> .env + echo "KAFKA_BROKER_ADDRESS=$KAFKA_BROKER_ADDRESS" >> .env + echo "KAFKA_BROKER_ID=$KAFKA_BROKER_ID" >> .env + echo "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=$KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" >> .env + echo "KAFKA_ADVERTISED_LISTENERS=$KAFKA_ADVERTISED_LISTENERS" >> .env + echo "KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=$KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" >> .env + echo "KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=$KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" >> .env + echo "KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=$KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" >> .env + echo "KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=$KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" >> .env + echo "KAFKA_PROCESS_ROLES=$KAFKA_PROCESS_ROLES" >> .env + echo "KAFKA_NODE_ID=$KAFKA_NODE_ID" >> .env + echo "KAFKA_CONTROLLER_QUORUM_VOTERS=$KAFKA_CONTROLLER_QUORUM_VOTERS" >> .env + echo "KAFKA_LISTENERS=$KAFKA_LISTENERS" >> .env + echo "KAFKA_INTER_BROKER_LISTENER_NAME=$KAFKA_INTER_BROKER_LISTENER_NAME" >> .env + echo "KAFKA_CONTROLLER_LISTENER_NAMES=$KAFKA_CONTROLLER_LISTENER_NAMES" >> .env + echo "CLUSTER_ID=$CLUSTER_ID" >> .env + echo "KAFKA_LOG4J_ROOT_LOGLEVEL=$KAFKA_LOG4J_ROOT_LOGLEVEL" >> .env + echo "KAFKA_LOG4J_LOGGERS=$KAFKA_LOG4J_LOGGERS" >> .env + echo "OLLAMA_URL=$OLLAMA_URL" >> .env + echo "LLM_MODEL=$LLM_MODEL" >> .env + echo "EMBED_URL=$EMBED_URL" >> .env + echo "EMBED_MODEL=$EMBED_MODEL" >> .env + echo "ZILLIZ_ADDRESS=$ZILLIZ_ADDRESS" >> .env + echo "ZILLIZ_API_KEY=$ZILLIZ_API_KEY" >> .env + echo "VECTOR_DIMENSION=$VECTOR_DIMENSION" >> .env + echo "CA_CRT=$CA_CRT" >> .env + echo "QUERY_CRT=$QUERY_CRT" >> .env + echo "QUERY_KEY=$QUERY_KEY" >> .env + echo "AUTH_CRT=$AUTH_CRT" >> .env + echo "AUTH_KEY=$AUTH_KEY" >> .env + echo "VECTOR_CRT=$VECTOR_CRT" >> .env + echo "VECTOR_KEY=$VECTOR_KEY" >> .env + echo "GATEWAY_CRT=$GATEWAY_CRT" >> .env + echo "GATEWAY_KEY=$GATEWAY_KEY" >> .env + echo "DESKTOP_CRT=$DESKTOP_CRT" >> .env + echo "DESKTOP_KEY=$DESKTOP_KEY" >> .env + echo "ALLOWED_CLIENT_IP=$ALLOWED_CLIENT_IP" >> .env + echo "EC2_KEY_PAIR=$EC2_KEY_PAIR" >> .env + echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env + echo "EC2_PUBLIC_HOST=$EC2_PUBLIC_HOST" >> .env + echo "ROOT_CA_CERT=$ROOT_CA_CERT" >> .env # run: | # chmod +x ./generate_env.sh @@ -170,7 +273,8 @@ jobs: # Copy the docker-compose file to EC2 scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - + scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + # SSH into the EC2 instance and run the Docker containers ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed From 66dc4286fa7aec9dd4897e81b3798234bd262b58 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 15:27:19 -0500 Subject: [PATCH 084/160] fix: fixed .env path --- .github/workflows/deploy copy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 9c637d21..95dfec6c 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -273,8 +273,8 @@ jobs: # Copy the docker-compose file to EC2 scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - + scp -o StrictHostKeyChecking=no -i private_key.pem .env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + # SSH into the EC2 instance and run the Docker containers ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed From ec90a9cd3e1de442f52cd2e1802914c85c958513 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sat, 8 Mar 2025 15:30:21 -0500 Subject: [PATCH 085/160] feat: added step to create dir --- .github/workflows/deploy copy.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/deploy copy.yaml index 95dfec6c..c3f0e033 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/deploy copy.yaml @@ -271,6 +271,14 @@ jobs: sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml sed -i "s|waitlistImage|$waitlistImage|g" backend/docker-compose-ec2.yaml + # SSH into EC2 to create directories first + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + # Create necessary directories if they don't exist + if [ ! -d "/home/ec2-user/common/config" ]; then + mkdir -p /home/ec2-user/common/config + fi + EOF + # Copy the docker-compose file to EC2 scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ scp -o StrictHostKeyChecking=no -i private_key.pem .env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env From f5868dad4ce14bc5847111951f319dc9fb372530 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 22:15:44 -0400 Subject: [PATCH 086/160] feat: removed waitlist instance from aws deployment, added model download command on EC2 instance --- .../workflows/{deploy copy.yaml => ECS_deploy.yaml} | 9 +-------- backend/docker-compose-ec2.yaml | 10 +--------- 2 files changed, 2 insertions(+), 17 deletions(-) rename .github/workflows/{deploy copy.yaml => ECS_deploy.yaml} (96%) diff --git a/.github/workflows/deploy copy.yaml b/.github/workflows/ECS_deploy.yaml similarity index 96% rename from .github/workflows/deploy copy.yaml rename to .github/workflows/ECS_deploy.yaml index c3f0e033..6b57e149 100644 --- a/.github/workflows/deploy copy.yaml +++ b/.github/workflows/ECS_deploy.yaml @@ -225,10 +225,6 @@ jobs: # docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - # docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend - # docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - - name: Deploy to EC2 run: | # Extract markers @@ -258,18 +254,15 @@ jobs: authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156" queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156" gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156" - waitlistImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/waitlist:13714734156" docker pull $authImage docker pull $queryImage docker pull $gatewayImage - docker pull $waitlistImage # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml - sed -i "s|waitlistImage|$waitlistImage|g" backend/docker-compose-ec2.yaml # SSH into EC2 to create directories first ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' @@ -324,6 +317,6 @@ jobs: # Run docker compose with absolute path docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d - + docker exec ec2-user-ollama-1 ollama pull deepseek-r1:1.5b # For later EOF diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml index 8908e4cf..354a1655 100644 --- a/backend/docker-compose-ec2.yaml +++ b/backend/docker-compose-ec2.yaml @@ -59,15 +59,7 @@ services: condition: service_healthy networks: - indeq-net - waitlist: - image: waitlistImage - env_file: - - ./common/config/.env - depends_on: - appDB: - condition: service_healthy - networks: - - indeq-net + appDB: image: postgres:latest env_file: From 9d709a2d76d33c4203e54ef34c7662b4b2490844 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 22:23:31 -0400 Subject: [PATCH 087/160] feat: added build steps and now testing whole cicd pipeline --- .../{ECS_deploy.yaml => EC2_deploy.yaml} | 84 +++++++++---------- 1 file changed, 40 insertions(+), 44 deletions(-) rename .github/workflows/{ECS_deploy.yaml => EC2_deploy.yaml} (84%) diff --git a/.github/workflows/ECS_deploy.yaml b/.github/workflows/EC2_deploy.yaml similarity index 84% rename from .github/workflows/ECS_deploy.yaml rename to .github/workflows/EC2_deploy.yaml index 6b57e149..63df1678 100644 --- a/.github/workflows/ECS_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -21,31 +21,31 @@ jobs: - name: Checkout Repository uses: actions/checkout@v2 - # - name: Install Protocol Buffers Compiler - # run: | - # sudo apt-get update - # sudo apt-get install -y protobuf-compiler + - name: Install Protocol Buffers Compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler - # - name: Install Go - # run: | - # sudo apt-get update - # sudo apt-get install -y golang-go + - name: Install Go + run: | + sudo apt-get update + sudo apt-get install -y golang-go - # - name: Install protoc-gen-go - # run: | - # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - # - name: Install protoc-gen-go-grpc - # run: | - # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + - name: Install protoc-gen-go-grpc + run: | + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - # - name: Check protoc-gen-go Installation - # run: | - # protoc-gen-go --version || echo "protoc-gen-go not found" + - name: Check protoc-gen-go Installation + run: | + protoc-gen-go --version || echo "protoc-gen-go not found" - name: Generate .env Files env: @@ -188,17 +188,13 @@ jobs: echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env echo "EC2_PUBLIC_HOST=$EC2_PUBLIC_HOST" >> .env echo "ROOT_CA_CERT=$ROOT_CA_CERT" >> .env - - # run: | - # chmod +x ./generate_env.sh - # ./generate_env.sh - # - name: Generate Code - # run: | - # cd backend/common && make gen + - name: Generate Code + run: | + cd backend/common && make gen - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 @@ -211,19 +207,19 @@ jobs: run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - # - name: Build and Push Docker Images - # run: | - # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - # docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + - name: Build and Push Docker Images + run: | + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - # docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - # docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - name: Deploy to EC2 run: | @@ -251,9 +247,9 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:13714734156" - queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:13714734156" - gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:13714734156" + authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:$IMAGE_TAG" + queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:$IMAGE_TAG" + gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:$IMAGE_TAG" docker pull $authImage docker pull $queryImage From bbea099e37d3b0b253220297c1fa20ff1ef022cf Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 22:27:31 -0400 Subject: [PATCH 088/160] fix: added step to move .env to right folder --- .github/workflows/EC2_deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 63df1678..cdb3e6f1 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -188,6 +188,7 @@ jobs: echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env echo "EC2_PUBLIC_HOST=$EC2_PUBLIC_HOST" >> .env echo "ROOT_CA_CERT=$ROOT_CA_CERT" >> .env + mv .env backend/common/config/.env - name: Generate Code run: | From 13bdab9edc2e5f5dece83d1f6cdf89efa4a247fa Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 22:31:49 -0400 Subject: [PATCH 089/160] fix: changed .env path for scp --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index cdb3e6f1..b5c4b29a 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -271,7 +271,7 @@ jobs: # Copy the docker-compose file to EC2 scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - scp -o StrictHostKeyChecking=no -i private_key.pem .env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env # SSH into the EC2 instance and run the Docker containers ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' From 1d155de83e3f726a74f6dd3c9f67f9db6e6163e7 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 23:00:19 -0400 Subject: [PATCH 090/160] fix: added command to clean up leftover resources to free up space --- .github/workflows/EC2_deploy.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index b5c4b29a..4a3686e4 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -303,6 +303,9 @@ jobs: docker rm $(docker ps -aq) fi + docker image prune -a + docker network prune -a + # Configure AWS CLI aws configure set region us-east-1 From 322f8389949bbbaf33bcec091c5d7c57695bab5e Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 23:09:52 -0400 Subject: [PATCH 091/160] fix: debugging cleanup --- .github/workflows/EC2_deploy.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 4a3686e4..dda717a4 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -240,11 +240,6 @@ jobs: sudo apt-get update sudo apt-get install -y awscli fi - - echo "Current directory: $(pwd)" - ls -la - ls -la backend/ - cat backend/docker-compose-ec2.yaml # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com @@ -261,6 +256,11 @@ jobs: sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml + echo "Current directory: $(pwd)" + ls -la + ls -la backend/ + cat backend/docker-compose-ec2.yaml + # SSH into EC2 to create directories first ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Create necessary directories if they don't exist @@ -303,8 +303,8 @@ jobs: docker rm $(docker ps -aq) fi - docker image prune -a - docker network prune -a + # docker image prune -a + # docker network prune -a # Configure AWS CLI aws configure set region us-east-1 From 95305c1e0e6660b0f6ef278514efae2ccf528f6e Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 23:19:17 -0400 Subject: [PATCH 092/160] fix: moved cleaning command to earlier step and increased debugging verbosity --- .github/workflows/EC2_deploy.yaml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index dda717a4..8a212cf9 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -247,10 +247,6 @@ jobs: queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:$IMAGE_TAG" gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:$IMAGE_TAG" - docker pull $authImage - docker pull $queryImage - docker pull $gatewayImage - # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml @@ -261,12 +257,20 @@ jobs: ls -la backend/ cat backend/docker-compose-ec2.yaml - # SSH into EC2 to create directories first - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + # SSH into EC2 to create directories first for clean up + ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Create necessary directories if they don't exist if [ ! -d "/home/ec2-user/common/config" ]; then mkdir -p /home/ec2-user/common/config fi + + # If directory exists, remove .env inside + if [ -f "/home/ec2-user/common/config/.env" ]; then + rm /home/ec2-user/common/config/.env + fi + + docker image prune -a + docker network prune -a EOF # Copy the docker-compose file to EC2 @@ -274,7 +278,7 @@ jobs: scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -vvv-o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." @@ -303,9 +307,6 @@ jobs: docker rm $(docker ps -aq) fi - # docker image prune -a - # docker network prune -a - # Configure AWS CLI aws configure set region us-east-1 From 1397547f8a8a9b853e067eb8bd43ad93e0e09cce Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 23:35:31 -0400 Subject: [PATCH 093/160] fix: split up commands into multiple tasks with debugging statements --- .github/workflows/EC2_deploy.yaml | 79 +++++++++++++++++-------------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 8a212cf9..fdd746fb 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -211,18 +211,26 @@ jobs: - name: Build and Push Docker Images run: | docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + authImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker tag authentication:$IMAGE_TAG $authImage + docker push $authImage docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + queryImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker tag query:$IMAGE_TAG $queryImage + docker push $queryImage docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + gatewayImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker tag gateway:$IMAGE_TAG $gatewayImage + docker push $gatewayImage - - name: Deploy to EC2 + # Replace placeholder image references with actual ECR URLs + sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml + sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml + sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml + + - name: Extract EC2 Key Pair run: | # Extract markers BEGIN_MARKER="-----BEGIN RSA PRIVATE KEY-----" @@ -234,31 +242,18 @@ jobs: echo "$END_MARKER" >> private_key.pem chmod 600 private_key.pem - # Install AWS CLI if not already installed - if ! command -v aws &> /dev/null; then - echo "AWS CLI not found. Installing..." - sudo apt-get update - sudo apt-get install -y awscli - fi - - # Copy the Docker image to the EC2 instance - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/authentication:$IMAGE_TAG" - queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/query:$IMAGE_TAG" - gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com/indeq/gateway:$IMAGE_TAG" - - # Replace placeholder image references with actual ECR URLs - sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml - sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml - sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml - - echo "Current directory: $(pwd)" - ls -la - ls -la backend/ - cat backend/docker-compose-ec2.yaml + - name: Predeploy Cleanup + run: | # SSH into EC2 to create directories first for clean up ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + + # Print current directory and list contents + pwd + ls -la + ls -la backend/ + cat backend/docker-compose-ec2.yaml + # Create necessary directories if they don't exist if [ ! -d "/home/ec2-user/common/config" ]; then mkdir -p /home/ec2-user/common/config @@ -269,14 +264,33 @@ jobs: rm /home/ec2-user/common/config/.env fi + # Stop and remove containers safely + if [ "$(docker ps -q)" ]; then + docker stop $(docker ps -q) + fi + if [ "$(docker ps -aq)" ]; then + docker rm $(docker ps -aq) + fi + docker image prune -a docker network prune -a EOF + - name: Copy docker-compose file to EC2 + run: | # Copy the docker-compose file to EC2 scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + - name: Deploy to EC2 + run: | + # Install AWS CLI if not already installed + if ! command -v aws &> /dev/null; then + echo "AWS CLI not found. Installing..." + sudo apt-get update + sudo apt-get install -y awscli + fi + # SSH into the EC2 instance and run the Docker containers ssh -vvv-o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed @@ -299,13 +313,6 @@ jobs: sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose fi - # Stop and remove containers safely - if [ "$(docker ps -q)" ]; then - docker stop $(docker ps -q) - fi - if [ "$(docker ps -aq)" ]; then - docker rm $(docker ps -aq) - fi # Configure AWS CLI aws configure set region us-east-1 From 4bcb4c30da6a7cebbc4dc83d8d0e8f6042915b50 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 23:42:22 -0400 Subject: [PATCH 094/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index fdd746fb..b57a1873 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -211,17 +211,17 @@ jobs: - name: Build and Push Docker Images run: | docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - authImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" docker tag authentication:$IMAGE_TAG $authImage docker push $authImage docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - queryImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" docker tag query:$IMAGE_TAG $queryImage docker push $queryImage docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - gatewayImage = ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" docker tag gateway:$IMAGE_TAG $gatewayImage docker push $gatewayImage From a6f99c90f10cf75ad141a55323c44319a00809ca Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Sun, 9 Mar 2025 23:47:46 -0400 Subject: [PATCH 095/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index b57a1873..1fadb992 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -292,7 +292,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -vvv-o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From 716c296998dd8e73df231fe4dc44a2052ce35c9c Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 10 Mar 2025 00:03:56 -0400 Subject: [PATCH 096/160] fix: removed verbose option --- .github/workflows/EC2_deploy.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 1fadb992..a83896ab 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -246,7 +246,7 @@ jobs: - name: Predeploy Cleanup run: | # SSH into EC2 to create directories first for clean up - ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Print current directory and list contents pwd @@ -292,20 +292,20 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." sudo yum update -y - sudo yum install docker -y + sudo yum install -y docker sudo systemctl start docker sudo systemctl enable docker - sudo usermod -a -G docker ec2-user + sudo usermod -aG docker ec2-user echo "Docker installed successfully" fi # Install Docker Compose V2 if not present - if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then + if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then echo "Installing Docker Compose V2..." # For Amazon Linux 2023 sudo mkdir -p /usr/local/lib/docker/cli-plugins @@ -313,7 +313,6 @@ jobs: sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose fi - # Configure AWS CLI aws configure set region us-east-1 @@ -327,4 +326,3 @@ jobs: docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d docker exec ec2-user-ollama-1 ollama pull deepseek-r1:1.5b # For later EOF - From fbcbfd22800ded76d3acf72d2201447a56b2c00c Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 10 Mar 2025 00:04:44 -0400 Subject: [PATCH 097/160] fix: commented out model download statement --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index a83896ab..0651bb3b 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -324,5 +324,5 @@ jobs: # Run docker compose with absolute path docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d - docker exec ec2-user-ollama-1 ollama pull deepseek-r1:1.5b # For later + # docker exec ec2-user-ollama-1 ollama pull deepseek-r1:1.5b # For later EOF From f4029da23edb82e6823a2955ac2e069c58648b3a Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 10 Mar 2025 00:27:43 -0400 Subject: [PATCH 098/160] fix: removed ollama --- .github/workflows/EC2_deploy.yaml | 2 +- backend/docker-compose-ec2.yaml | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 0651bb3b..38af74b7 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -324,5 +324,5 @@ jobs: # Run docker compose with absolute path docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d - # docker exec ec2-user-ollama-1 ollama pull deepseek-r1:1.5b # For later + EOF diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml index 354a1655..e4c55688 100644 --- a/backend/docker-compose-ec2.yaml +++ b/backend/docker-compose-ec2.yaml @@ -8,17 +8,17 @@ services: - indeq-net # this docker setup doesn't utilize the GPU - ollama: - image: ollama/ollama:latest - ports: - - "11434:11434" - volumes: - - ollama_models:/root/.ollama - environment: - - OLLAMA_NUM_PARALLEL=4 # Number of threads to utilize - mem_limit: 16g - networks: - - indeq-net + # ollama: + # image: ollama/ollama:latest + # ports: + # - "11434:11434" + # volumes: + # - ollama_models:/root/.ollama + # environment: + # - OLLAMA_NUM_PARALLEL=4 # Number of threads to utilize + # mem_limit: 16g + # networks: + # - indeq-net rabbitmq: image: rabbitmq:3-management From 4fbb4c74f867a042df637f91ebcaefef18a711c7 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 10 Mar 2025 23:36:03 -0400 Subject: [PATCH 099/160] fix: added back vercel analytics --- frontend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/package.json b/frontend/package.json index f531af1f..c8bd7b5a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,6 +34,7 @@ "vite": "^6.0.0" }, "dependencies": { + "@vercel/analytics": "^1.5.0", "mongodb": "^6.14.2", "svelte-feather-icons": "^4.2.0" } From 2f81aaf7349cf9e9ded596bec4b42dc17b6f8784 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 10 Mar 2025 23:53:13 -0400 Subject: [PATCH 100/160] fix: debugging ssh deployment --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 38af74b7..517fd1a4 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -292,7 +292,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o -vvv StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From fa1dd8276cb90010235560fd43e20fa35738452e Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 11 Mar 2025 00:33:24 -0400 Subject: [PATCH 101/160] fix: added -f flag to not have to press y --- .github/workflows/EC2_deploy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 517fd1a4..c3dd22eb 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -272,8 +272,8 @@ jobs: docker rm $(docker ps -aq) fi - docker image prune -a - docker network prune -a + docker image prune -a -f + docker network prune -a -f EOF - name: Copy docker-compose file to EC2 @@ -292,7 +292,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -o -vvv StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From d88db7f93947e40508b0d8fab6ad5ba3e1112982 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 11 Mar 2025 00:40:54 -0400 Subject: [PATCH 102/160] fix: removed -a --- .github/workflows/EC2_deploy.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index c3dd22eb..69dbf595 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -250,9 +250,6 @@ jobs: # Print current directory and list contents pwd - ls -la - ls -la backend/ - cat backend/docker-compose-ec2.yaml # Create necessary directories if they don't exist if [ ! -d "/home/ec2-user/common/config" ]; then @@ -272,8 +269,8 @@ jobs: docker rm $(docker ps -aq) fi - docker image prune -a -f - docker network prune -a -f + docker image prune -f + docker network prune -f EOF - name: Copy docker-compose file to EC2 From 575f914ae49d0a5809473645fc426193b444f14f Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Mon, 21 Apr 2025 23:37:04 -0400 Subject: [PATCH 103/160] feat: added new images --- .github/deploy.yaml | 223 +++++++++++++++++++++++++------------------- 1 file changed, 126 insertions(+), 97 deletions(-) diff --git a/.github/deploy.yaml b/.github/deploy.yaml index 910f230d..2d8ea566 100644 --- a/.github/deploy.yaml +++ b/.github/deploy.yaml @@ -18,84 +18,84 @@ jobs: contents: read steps: - # - name: Checkout Repository - # uses: actions/checkout@v2 - - # - name: Install Protocol Buffers Compiler - # run: | - # sudo apt-get update - # sudo apt-get install -y protobuf-compiler - - # - name: Install Go - # run: | - # sudo apt-get update - # sudo apt-get install -y golang-go - - # - name: Install protoc-gen-go - # run: | - # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - # - name: Install protoc-gen-go-grpc - # run: | - # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - # - name: Check protoc-gen-go Installation - # run: | - # protoc-gen-go --version || echo "protoc-gen-go not found" - - # - name: Generate .env Files - # env: - # AUTH_PORT: ${{ secrets.AUTH_PORT }} - # AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} - # QUERY_PORT: ${{ secrets.QUERY_PORT }} - # QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} - # VECTOR_PORT: ${{ secrets.VECTOR_PORT }} - # VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} - # GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} - # DATABASE_URL: ${{ secrets.DATABASE_URL }} - # POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - # POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - # POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - # JWT_SECRET: ${{ secrets.JWT_SECRET }} - # ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} - # ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} - # ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} - # ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} - # ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} - # MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} - # MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} - # MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} - # RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} - # RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} - # RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} - # RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} - # OLLAMA_URL: ${{ secrets.OLLAMA_URL }} - # LLM_MODEL: ${{ secrets.LLM_MODEL }} - # ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} - # ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} - # CA_CRT: ${{ secrets.CA_CRT }} - # QUERY_CRT: ${{ secrets.QUERY_CRT }} - # QUERY_KEY: ${{ secrets.QUERY_KEY }} - # AUTH_CRT: ${{ secrets.AUTH_CRT }} - # AUTH_KEY: ${{ secrets.AUTH_KEY }} - # GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} - # GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} - # ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Install Protocol Buffers Compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler + + - name: Install Go + run: | + sudo apt-get update + sudo apt-get install -y golang-go + + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Install protoc-gen-go-grpc + run: | + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Check protoc-gen-go Installation + run: | + protoc-gen-go --version || echo "protoc-gen-go not found" + + - name: Generate .env Files + env: + AUTH_PORT: ${{ secrets.AUTH_PORT }} + AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + QUERY_PORT: ${{ secrets.QUERY_PORT }} + QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + CA_CRT: ${{ secrets.CA_CRT }} + QUERY_CRT: ${{ secrets.QUERY_CRT }} + QUERY_KEY: ${{ secrets.QUERY_KEY }} + AUTH_CRT: ${{ secrets.AUTH_CRT }} + AUTH_KEY: ${{ secrets.AUTH_KEY }} + GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} - # run: | - # chmod +x ./generate_env.sh - # ./generate_env.sh + run: | + chmod +x ./generate_env.sh + ./generate_env.sh - # - name: Generate Code - # run: | - # cd backend/common && make gen + - name: Generate Code + run: | + cd backend/common && make gen - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 @@ -108,24 +108,51 @@ jobs: run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - # - name: Build and Push Docker Images - # run: | - # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - # docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + - name: Build and Push Docker Images + run: | + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - # docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend + docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - # docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + docker tag init:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG - # docker build -t waitlist:$IMAGE_TAG -f backend/waitlist/Dockerfile backend - # docker tag waitlist:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG - # docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + docker tag embedding:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG + + docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + docker tag retrieval:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG + + docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + docker tag vector:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG + + docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + docker tag desktop:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG + + docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + docker tag crawling:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG + docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + docker tag integration:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG + docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG - name: Deploy to EC2 run: | @@ -141,18 +168,20 @@ jobs: # Copy the Docker image to the EC2 instance aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 - docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG echo "Content of the PEM file:" # SSH into the EC2 instance and run the Docker containers ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' - docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:13714734156 - docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:13714734156 - docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:13714734156 - docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:13714734156 + docker run -d --restart always --name authentication -p 80:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG + docker run -d --restart always --name query -p 81:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG + docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG + docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG + docker run -d --restart always --name init -p 84:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG EOF From b23e53c8a63e65f52d192cbf14fb025f96c81997 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 00:17:55 -0400 Subject: [PATCH 104/160] feat: revised .env --- .github/deploy.yaml | 105 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/.github/deploy.yaml b/.github/deploy.yaml index 2d8ea566..f1635221 100644 --- a/.github/deploy.yaml +++ b/.github/deploy.yaml @@ -50,16 +50,55 @@ jobs: - name: Generate .env Files env: AUTH_PORT: ${{ secrets.AUTH_PORT }} + DEV_PROD: ${{ secrets.DEV_PROD }} AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} QUERY_PORT: ${{ secrets.QUERY_PORT }} QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} VECTOR_PORT: ${{ secrets.VECTOR_PORT }} VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} + DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} + RETRIEVAL_ADDRESS: ${{ secrets.RETRIEVAL_ADDRESS }} + RETRIEVAL_PORT: ${{ secrets.RETRIEVAL_PORT }} + WAITLIST_ADDRESS: ${{ secrets.WAITLIST_ADDRESS }} + WAITLIST_PORT: ${{ secrets.WAITLIST_PORT }} + INTEGRATION_ADDRESS: ${{ secrets.INTEGRATION_ADDRESS }} + INTEGRATION_PORT: ${{ secrets.INTEGRATION_PORT }} GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + CRAWLING_PORT: ${{ secrets.CRAWLING_PORT }} + CRAWLING_ADDRESS: ${{ secrets.CRAWLING_ADDRESS }} + COUCHDB_ADDRESS: ${{ secrets.COUCHDB_ADDRESS }} + MQTT_ADDRESS: ${{ secrets.MQTT_ADDRESS }} + MQTT_PORT: ${{ secrets.MQTT_PORT }} + EMBEDDING_MODEL_NAME: ${{ secrets.EMBEDDING_MODEL_NAME }} + EMBEDDING_RERANKER: ${{ secrets.EMBEDDING_RERANKER }} + EMBEDDING_PORT: ${{ secrets.EMBEDDING_PORT }} + EMBEDDING_ADDRESS: ${{ secrets.EMBEDDING_ADDRESS }} + EMBEDDING_MAX_WORKERS: ${{ secrets.EMBEDDING_MAX_WORKERS }} + EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT: ${{ secrets.EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT }} + CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS }} + CRAWLING_GMAIL_MAX_WORKERS: ${{ secrets.CRAWLING_GMAIL_MAX_WORKERS }} + CRAWLING_DRIVE_MAX_WORKERS: ${{ secrets.CRAWLING_DRIVE_MAX_WORKERS }} + CRAWLING_GOOGLEDOCS_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEDOCS_MAX_WORKERS }} + CRAWLING_GOOGLESLIDES_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLESLIDES_MAX_WORKERS }} + CRAWLING_GOOGLEPDF_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEPDF_MAX_WORKERS }} + CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS }} + VECTOR_BATCH_SIZE: ${{ secrets.VECTOR_BATCH_SIZE }} + VECTOR_BATCH_TIMEOUT: ${{ secrets.VECTOR_BATCH_TIMEOUT }} + QUERY_NUMBER_OF_SOURCES: ${{ secrets.QUERY_NUMBER_OF_SOURCES }} + QUERY_TTL: ${{ secrets.QUERY_TTL }} + QUERY_QUEUE_TTL: ${{ secrets.QUERY_QUEUE_TTL }} + QUERY_EXPANSION: ${{ secrets.QUERY_EXPANSION }} + QUERY_SUMMARY_UPPER_BOUND: ${{ secrets.QUERY_SUMMARY_UPPER_BOUND }} + QUERY_SUMMARY_LOWER_BOUND: ${{ secrets.QUERY_SUMMARY_LOWER_BOUND }} + QUERY_DEFAULT_MODEL: ${{ secrets.QUERY_DEFAULT_MODEL }} + RETRIEVAL_K_VAL: ${{ secrets.RETRIEVAL_K_VAL }} DATABASE_URL: ${{ secrets.DATABASE_URL }} POSTGRES_USER: ${{ secrets.POSTGRES_USER }} POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + COUCHDB_USER: ${{ secrets.COUCHDB_USER }} + COUCHDB_PASSWORD: ${{ secrets.COUCHDB_PASSWORD }} JWT_SECRET: ${{ secrets.JWT_SECRET }} ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} @@ -73,18 +112,84 @@ jobs: RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + KAFKA_BROKER_ADDRESS: ${{ secrets.KAFKA_BROKER_ADDRESS }} + KAFKA_BROKER_ID: ${{ secrets.KAFKA_BROKER_ID }} + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${{ secrets.KAFKA_LISTENER_SECURITY_PROTOCOL_MAP }} + KAFKA_ADVERTISED_LISTENERS: ${{ secrets.KAFKA_ADVERTISED_LISTENERS }} + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${{ secrets.KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR }} + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: ${{ secrets.KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS }} + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_MIN_ISR }} + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR }} + KAFKA_PROCESS_ROLES: ${{ secrets.KAFKA_PROCESS_ROLES }} + KAFKA_NODE_ID: ${{ secrets.KAFKA_NODE_ID }} + KAFKA_CONTROLLER_QUORUM_VOTERS: ${{ secrets.KAFKA_CONTROLLER_QUORUM_VOTERS }} + KAFKA_LISTENERS: ${{ secrets.KAFKA_LISTENERS }} + KAFKA_INTER_BROKER_LISTENER_NAME: ${{ secrets.KAFKA_INTER_BROKER_LISTENER_NAME }} + KAFKA_CONTROLLER_LISTENER_NAMES: ${{ secrets.KAFKA_CONTROLLER_LISTENER_NAMES }} + CLUSTER_ID: ${{ secrets.CLUSTER_ID }} + KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} + KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} LLM_MODEL: ${{ secrets.LLM_MODEL }} ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} + GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} + GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} + GOOGLE_AUTH_URL: ${{ secrets.GOOGLE_AUTH_URL }} + GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} + GOOGLE_SCOPES: ${{ secrets.GOOGLE_SCOPES }} + GOOGLE_SSO_CLIENT_ID: ${{ secrets.GOOGLE_SSO_CLIENT_ID }} + GOOGLE_SSO_CLIENT_SECRET: ${{ secrets.GOOGLE_SSO_CLIENT_SECRET }} + GOOGLE_SSO_REDIRECT_URI: ${{ secrets.GOOGLE_SSO_REDIRECT_URI }} + GOOGLE_SSO_SCOPES: ${{ secrets.GOOGLE_SSO_SCOPES }} + MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} + MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} + MICROSOFT_AUTH_URL: ${{ secrets.MICROSOFT_AUTH_URL }} + MICROSOFT_REDIRECT_URI: ${{ secrets.MICROSOFT_REDIRECT_URI }} + MICROSOFT_SCOPES: ${{ secrets.MICROSOFT_SCOPES }} + NOTION_CLIENT_ID: ${{ secrets.NOTION_CLIENT_ID }} + NOTION_CLIENT_SECRET: ${{ secrets.NOTION_CLIENT_SECRET }} + NOTION_REDIRECT_URI: ${{ secrets.NOTION_REDIRECT_URI }} + NOTION_AUTH_URL: ${{ secrets.NOTION_AUTH_URL }} + REDIS_ADDRESS: ${{ secrets.REDIS_ADDRESS }} + REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} + ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} CA_CRT: ${{ secrets.CA_CRT }} + CA_KEY: ${{ secrets.CA_KEY }} QUERY_CRT: ${{ secrets.QUERY_CRT }} QUERY_KEY: ${{ secrets.QUERY_KEY }} AUTH_CRT: ${{ secrets.AUTH_CRT }} AUTH_KEY: ${{ secrets.AUTH_KEY }} + VECTOR_CRT: ${{ secrets.VECTOR_CRT }} + VECTOR_KEY: ${{ secrets.VECTOR_KEY }} GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} + DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} + EMBEDDING_CRT: ${{ secrets.EMBEDDING_CRT }} + EMBEDDING_KEY: ${{ secrets.EMBEDDING_KEY }} + RETRIEVAL_CRT: ${{ secrets.RETRIEVAL_CRT }} + RETRIEVAL_KEY: ${{ secrets.RETRIEVAL_KEY }} + MQTT_CRT: ${{ secrets.MQTT_CRT }} + MQTT_KEY: ${{ secrets.MQTT_KEY }} + WAITLIST_CRT: ${{ secrets.WAITLIST_CRT }} + WAITLIST_KEY: ${{ secrets.WAITLIST_KEY }} + INTEGRATION_CRT: ${{ secrets.INTEGRATION_CRT }} + INTEGRATION_KEY: ${{ secrets.INTEGRATION_KEY }} + CRAWLING_CRT: ${{ secrets.CRAWLING_CRT }} + CRAWLING_KEY: ${{ secrets.CRAWLING_KEY }} ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + SMTP_FROM: ${{ secrets.SMTP_FROM }} + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + SMTP_HOST: ${{ secrets.SMTP_HOST }} + SMTP_PORT: ${{ secrets.SMTP_PORT }} + EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} + EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} run: | chmod +x ./generate_env.sh From 6d1ad87e4482e49816ddf3dbf622267e77d43caf Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 00:28:00 -0400 Subject: [PATCH 105/160] feat: added images to EC2 --- .github/deploy.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/deploy.yaml b/.github/deploy.yaml index f1635221..d68b593f 100644 --- a/.github/deploy.yaml +++ b/.github/deploy.yaml @@ -278,6 +278,12 @@ jobs: docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG + docker pull ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG echo "Content of the PEM file:" @@ -288,5 +294,10 @@ jobs: docker run -d --restart always --name gateway -p 82:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG docker run -d --restart always --name waitlist -p 83:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/waitlist:$IMAGE_TAG docker run -d --restart always --name init -p 84:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG + docker run -d --restart always --name embedding -p 85:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG + docker run -d --restart always --name retrieval -p 86:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG + docker run -d --restart always --name vector -p 87:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG + docker run -d --restart always --name desktop -p 88:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG + docker run -d --restart always --name crawling -p 89:80 ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG EOF From 95b137f05ab334420f2b60f2086c96d06f759839 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 00:34:49 -0400 Subject: [PATCH 106/160] feat: added all images to proper yaml --- .github/EKS_deploy.yaml | 185 ------------------------- .github/workflows/EC2_deploy.yaml | 216 +++++++++++++++++++++++++++--- 2 files changed, 201 insertions(+), 200 deletions(-) delete mode 100644 .github/EKS_deploy.yaml diff --git a/.github/EKS_deploy.yaml b/.github/EKS_deploy.yaml deleted file mode 100644 index c56b144f..00000000 --- a/.github/EKS_deploy.yaml +++ /dev/null @@ -1,185 +0,0 @@ -name: Deploy to Amazon EKS -# on: -# push: -# branches: -# - cicd # Trigger deployment on the cicd branch (currently deactivated) - -env: - AWS_REGION: us-east-1 - EKS_CLUSTER_NAME: backend-cluster - ECR_REPOSITORY_NAMESPACE: indeq - IMAGE_TAG: ${{ github.run_id }} - -jobs: - deploy: - runs-on: ubuntu-latest - permissions: - id-token: write # For OIDC auth (recommended) - contents: read - - steps: - - name: Checkout Repository - uses: actions/checkout@v2 - - - name: Install Protocol Buffers Compiler - run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler - - - name: Install Go - run: | - sudo apt-get update - sudo apt-get install -y golang-go - - - name: Install protoc-gen-go - run: | - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Install protoc-gen-go-grpc - run: | - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Check protoc-gen-go Installation - run: | - protoc-gen-go --version || echo "protoc-gen-go not found" - - - name: Generate .env Files - env: - AUTH_PORT: ${{ secrets.AUTH_PORT }} - AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} - QUERY_PORT: ${{ secrets.QUERY_PORT }} - QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} - VECTOR_PORT: ${{ secrets.VECTOR_PORT }} - VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} - GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - JWT_SECRET: ${{ secrets.JWT_SECRET }} - ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} - ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} - ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} - ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} - ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} - MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} - MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} - MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} - RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} - RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} - RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} - RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} - OLLAMA_URL: ${{ secrets.OLLAMA_URL }} - LLM_MODEL: ${{ secrets.LLM_MODEL }} - ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} - ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} - CA_CRT: ${{ secrets.CA_CRT }} - QUERY_CRT: ${{ secrets.QUERY_CRT }} - QUERY_KEY: ${{ secrets.QUERY_KEY }} - AUTH_CRT: ${{ secrets.AUTH_CRT }} - AUTH_KEY: ${{ secrets.AUTH_KEY }} - GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} - GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} - ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} - - run: | - chmod +x ./generate_env.sh - ./generate_env.sh - - - name: Generate Code - run: | - cd backend/common && make gen - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION }} - - - name: Update kubeconfig - run: | - aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION - - - name: Login to Amazon ECR - run: | - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - - - name: Build and Push Docker Images - run: | - docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - docker tag authentication:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG - - docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - docker tag query:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG - - docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - docker tag gateway:$IMAGE_TAG ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - docker push ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG - - - name: Create config.yaml with GitHub secrets - run: | - echo "apiVersion: v1 - kind: ConfigMap - metadata: - name: backend-config - data: - AUTH_PORT: \"\${{ secrets.AUTH_PORT }}\" - AUTH_ADDRESS: \"\${{ secrets.AUTH_ADDRESS }}\" - QUERY_PORT: \"\${{ secrets.QUERY_PORT }}\" - QUERY_ADDRESS: \"\${{ secrets.QUERY_ADDRESS }}\" - VECTOR_PORT: \"\${{ secrets.VECTOR_PORT }}\" - VECTOR_ADDRESS: \"\${{ secrets.VECTOR_ADDRESS }}\" - GATEWAY_ADDRESS: \"\${{ secrets.GATEWAY_ADDRESS }}\" - DATABASE_URL: \"\${{ secrets.DATABASE_URL }}\" - POSTGRES_USER: \"\${{ secrets.POSTGRES_USER }}\" - POSTGRES_PASSWORD: \"\${{ secrets.POSTGRES_PASSWORD }}\" - POSTGRES_DB: \"\${{ secrets.POSTGRES_DB }}\" - JWT_SECRET: \"\${{ secrets.JWT_SECRET }}\" - ARGON2_MEMORY: \"\${{ secrets.ARGON2_MEMORY }}\" - ARGON2_ITERATIONS: \"\${{ secrets.ARGON2_ITERATIONS }}\" - ARGON2_PARALLELISM: \"\${{ secrets.ARGON2_PARALLELISM }}\" - ARGON2_SALT_LENGTH: \"\${{ secrets.ARGON2_SALT_LENGTH }}\" - ARGON2_KEY_LENGTH: \"\${{ secrets.ARGON2_KEY_LENGTH }}\" - MIN_PASSWORD_LENGTH: \"\${{ secrets.MIN_PASSWORD_LENGTH }}\" - MAX_PASSWORD_LENGTH: \"\${{ secrets.MAX_PASSWORD_LENGTH }}\" - MAX_EMAIL_LENGTH: \"\${{ secrets.MAX_EMAIL_LENGTH }}\" - RABBITMQ_URL: \"\${{ secrets.RABBITMQ_URL }}\" - RABBITMQ_DEFAULT_USER: \"\${{ secrets.RABBITMQ_DEFAULT_USER }}\" - RABBITMQ_DEFAULT_PASS: \"\${{ secrets.RABBITMQ_DEFAULT_PASS }}\" - RABBITMQ_LOGS: \"\${{ secrets.RABBITMQ_LOGS }}\" - OLLAMA_URL: \"\${{ secrets.OLLAMA_URL }}\" - LLM_MODEL: \"\${{ secrets.LLM_MODEL }}\" - ZILLIZ_ADDRESS: \"\${{ secrets.ZILLIZ_ADDRESS }}\" - ZILLIZ_API_KEY: \"\${{ secrets.ZILLIZ_API_KEY }}\" - CA_CRT: \"\${{ secrets.CA_CRT }}\" - QUERY_CRT: \"\${{ secrets.QUERY_CRT }}\" - QUERY_KEY: \"\${{ secrets.QUERY_KEY }}\" - AUTH_CRT: \"\${{ secrets.AUTH_CRT }}\" - AUTH_KEY: \"\${{ secrets.AUTH_KEY }}\" - ALLOWED_CLIENT_IP: \"\${{ secrets.ALLOWED_CLIENT_IP }}\" - " > configmap.yaml - - - name: Configure kubectl - run: | - aws eks update-kubeconfig --name $EKS_CLUSTER_NAME --region $AWS_REGION - - - name: Deploy to EKS - run: | - kubectl version --short - kubectl apply -f backend/k8s-manifest.yml - kubectl apply -f configmap.yaml - - - name: Debug Environment Variables - run: | - echo "Current GOPATH: $(go env GOPATH)" - echo "Current PATH: $PATH" diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 69dbf595..96f0775b 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -49,8 +49,8 @@ jobs: - name: Generate .env Files env: - DEV_PROD: ${{ secrets.DEV_PROD }} AUTH_PORT: ${{ secrets.AUTH_PORT }} + DEV_PROD: ${{ secrets.DEV_PROD }} AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} QUERY_PORT: ${{ secrets.QUERY_PORT }} QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} @@ -58,13 +58,47 @@ jobs: VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} - DESKTOP_HTTP_PORT: ${{ secrets.DESKTOP_HTTP_PORT }} - DESKTOP_HTTP_ADDRESS: ${{ secrets.DESKTOP_HTTP_ADDRESS }} + RETRIEVAL_ADDRESS: ${{ secrets.RETRIEVAL_ADDRESS }} + RETRIEVAL_PORT: ${{ secrets.RETRIEVAL_PORT }} + WAITLIST_ADDRESS: ${{ secrets.WAITLIST_ADDRESS }} + WAITLIST_PORT: ${{ secrets.WAITLIST_PORT }} + INTEGRATION_ADDRESS: ${{ secrets.INTEGRATION_ADDRESS }} + INTEGRATION_PORT: ${{ secrets.INTEGRATION_PORT }} GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + CRAWLING_PORT: ${{ secrets.CRAWLING_PORT }} + CRAWLING_ADDRESS: ${{ secrets.CRAWLING_ADDRESS }} + COUCHDB_ADDRESS: ${{ secrets.COUCHDB_ADDRESS }} + MQTT_ADDRESS: ${{ secrets.MQTT_ADDRESS }} + MQTT_PORT: ${{ secrets.MQTT_PORT }} + EMBEDDING_MODEL_NAME: ${{ secrets.EMBEDDING_MODEL_NAME }} + EMBEDDING_RERANKER: ${{ secrets.EMBEDDING_RERANKER }} + EMBEDDING_PORT: ${{ secrets.EMBEDDING_PORT }} + EMBEDDING_ADDRESS: ${{ secrets.EMBEDDING_ADDRESS }} + EMBEDDING_MAX_WORKERS: ${{ secrets.EMBEDDING_MAX_WORKERS }} + EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT: ${{ secrets.EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT }} + CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS }} + CRAWLING_GMAIL_MAX_WORKERS: ${{ secrets.CRAWLING_GMAIL_MAX_WORKERS }} + CRAWLING_DRIVE_MAX_WORKERS: ${{ secrets.CRAWLING_DRIVE_MAX_WORKERS }} + CRAWLING_GOOGLEDOCS_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEDOCS_MAX_WORKERS }} + CRAWLING_GOOGLESLIDES_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLESLIDES_MAX_WORKERS }} + CRAWLING_GOOGLEPDF_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEPDF_MAX_WORKERS }} + CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS }} + VECTOR_BATCH_SIZE: ${{ secrets.VECTOR_BATCH_SIZE }} + VECTOR_BATCH_TIMEOUT: ${{ secrets.VECTOR_BATCH_TIMEOUT }} + QUERY_NUMBER_OF_SOURCES: ${{ secrets.QUERY_NUMBER_OF_SOURCES }} + QUERY_TTL: ${{ secrets.QUERY_TTL }} + QUERY_QUEUE_TTL: ${{ secrets.QUERY_QUEUE_TTL }} + QUERY_EXPANSION: ${{ secrets.QUERY_EXPANSION }} + QUERY_SUMMARY_UPPER_BOUND: ${{ secrets.QUERY_SUMMARY_UPPER_BOUND }} + QUERY_SUMMARY_LOWER_BOUND: ${{ secrets.QUERY_SUMMARY_LOWER_BOUND }} + QUERY_DEFAULT_MODEL: ${{ secrets.QUERY_DEFAULT_MODEL }} + RETRIEVAL_K_VAL: ${{ secrets.RETRIEVAL_K_VAL }} DATABASE_URL: ${{ secrets.DATABASE_URL }} POSTGRES_USER: ${{ secrets.POSTGRES_USER }} POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + COUCHDB_USER: ${{ secrets.COUCHDB_USER }} + COUCHDB_PASSWORD: ${{ secrets.COUCHDB_PASSWORD }} JWT_SECRET: ${{ secrets.JWT_SECRET }} ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} @@ -96,13 +130,36 @@ jobs: KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} LLM_MODEL: ${{ secrets.LLM_MODEL }} - EMBED_URL: ${{ secrets.EMBED_URL }} - EMBED_MODEL: ${{ secrets.EMBED_MODEL }} ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} + GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} + GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} + GOOGLE_AUTH_URL: ${{ secrets.GOOGLE_AUTH_URL }} + GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} + GOOGLE_SCOPES: ${{ secrets.GOOGLE_SCOPES }} + GOOGLE_SSO_CLIENT_ID: ${{ secrets.GOOGLE_SSO_CLIENT_ID }} + GOOGLE_SSO_CLIENT_SECRET: ${{ secrets.GOOGLE_SSO_CLIENT_SECRET }} + GOOGLE_SSO_REDIRECT_URI: ${{ secrets.GOOGLE_SSO_REDIRECT_URI }} + GOOGLE_SSO_SCOPES: ${{ secrets.GOOGLE_SSO_SCOPES }} + MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} + MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} + MICROSOFT_AUTH_URL: ${{ secrets.MICROSOFT_AUTH_URL }} + MICROSOFT_REDIRECT_URI: ${{ secrets.MICROSOFT_REDIRECT_URI }} + MICROSOFT_SCOPES: ${{ secrets.MICROSOFT_SCOPES }} + NOTION_CLIENT_ID: ${{ secrets.NOTION_CLIENT_ID }} + NOTION_CLIENT_SECRET: ${{ secrets.NOTION_CLIENT_SECRET }} + NOTION_REDIRECT_URI: ${{ secrets.NOTION_REDIRECT_URI }} + NOTION_AUTH_URL: ${{ secrets.NOTION_AUTH_URL }} + REDIS_ADDRESS: ${{ secrets.REDIS_ADDRESS }} + REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} + ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} CA_CRT: ${{ secrets.CA_CRT }} + CA_KEY: ${{ secrets.CA_KEY }} QUERY_CRT: ${{ secrets.QUERY_CRT }} QUERY_KEY: ${{ secrets.QUERY_KEY }} AUTH_CRT: ${{ secrets.AUTH_CRT }} @@ -113,14 +170,29 @@ jobs: GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} + EMBEDDING_CRT: ${{ secrets.EMBEDDING_CRT }} + EMBEDDING_KEY: ${{ secrets.EMBEDDING_KEY }} + RETRIEVAL_CRT: ${{ secrets.RETRIEVAL_CRT }} + RETRIEVAL_KEY: ${{ secrets.RETRIEVAL_KEY }} + MQTT_CRT: ${{ secrets.MQTT_CRT }} + MQTT_KEY: ${{ secrets.MQTT_KEY }} + WAITLIST_CRT: ${{ secrets.WAITLIST_CRT }} + WAITLIST_KEY: ${{ secrets.WAITLIST_KEY }} + INTEGRATION_CRT: ${{ secrets.INTEGRATION_CRT }} + INTEGRATION_KEY: ${{ secrets.INTEGRATION_KEY }} + CRAWLING_CRT: ${{ secrets.CRAWLING_CRT }} + CRAWLING_KEY: ${{ secrets.CRAWLING_KEY }} ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + SMTP_FROM: ${{ secrets.SMTP_FROM }} + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + SMTP_HOST: ${{ secrets.SMTP_HOST }} + SMTP_PORT: ${{ secrets.SMTP_PORT }} EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} - EC2_PUBLIC_HOST: ${{ secrets.EC2_PUBLIC_HOST }} - ROOT_CA_CERT: ${{ secrets.ROOT_CA_CERT }} run: | - echo "DEV_PROD=$DEV_PROD" > .env - echo "AUTH_PORT=$AUTH_PORT" >> .env + echo "AUTH_PORT=$AUTH_PORT" > .env + echo "DEV_PROD=$DEV_PROD" >> .env echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> .env echo "QUERY_PORT=$QUERY_PORT" >> .env echo "QUERY_ADDRESS=$QUERY_ADDRESS" >> .env @@ -128,13 +200,47 @@ jobs: echo "VECTOR_ADDRESS=$VECTOR_ADDRESS" >> .env echo "DESKTOP_ADDRESS=$DESKTOP_ADDRESS" >> .env echo "DESKTOP_PORT=$DESKTOP_PORT" >> .env - echo "DESKTOP_HTTP_PORT=$DESKTOP_HTTP_PORT" >> .env - echo "DESKTOP_HTTP_ADDRESS=$DESKTOP_HTTP_ADDRESS" >> .env + echo "RETRIEVAL_ADDRESS=$RETRIEVAL_ADDRESS" >> .env + echo "RETRIEVAL_PORT=$RETRIEVAL_PORT" >> .env + echo "WAITLIST_ADDRESS=$WAITLIST_ADDRESS" >> .env + echo "WAITLIST_PORT=$WAITLIST_PORT" >> .env + echo "INTEGRATION_ADDRESS=$INTEGRATION_ADDRESS" >> .env + echo "INTEGRATION_PORT=$INTEGRATION_PORT" >> .env echo "GATEWAY_ADDRESS=$GATEWAY_ADDRESS" >> .env + echo "CRAWLING_PORT=$CRAWLING_PORT" >> .env + echo "CRAWLING_ADDRESS=$CRAWLING_ADDRESS" >> .env + echo "COUCHDB_ADDRESS=$COUCHDB_ADDRESS" >> .env + echo "MQTT_ADDRESS=$MQTT_ADDRESS" >> .env + echo "MQTT_PORT=$MQTT_PORT" >> .env + echo "EMBEDDING_MODEL_NAME=$EMBEDDING_MODEL_NAME" >> .env + echo "EMBEDDING_RERANKER=$EMBEDDING_RERANKER" >> .env + echo "EMBEDDING_PORT=$EMBEDDING_PORT" >> .env + echo "EMBEDDING_ADDRESS=$EMBEDDING_ADDRESS" >> .env + echo "EMBEDDING_MAX_WORKERS=$EMBEDDING_MAX_WORKERS" >> .env + echo "EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT=$EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT" >> .env + echo "CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS=$CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS" >> .env + echo "CRAWLING_GMAIL_MAX_WORKERS=$CRAWLING_GMAIL_MAX_WORKERS" >> .env + echo "CRAWLING_DRIVE_MAX_WORKERS=$CRAWLING_DRIVE_MAX_WORKERS" >> .env + echo "CRAWLING_GOOGLEDOCS_MAX_WORKERS=$CRAWLING_GOOGLEDOCS_MAX_WORKERS" >> .env + echo "CRAWLING_GOOGLESLIDES_MAX_WORKERS=$CRAWLING_GOOGLESLIDES_MAX_WORKERS" >> .env + echo "CRAWLING_GOOGLEPDF_MAX_WORKERS=$CRAWLING_GOOGLEPDF_MAX_WORKERS" >> .env + echo "CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS=$CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS" >> .env + echo "VECTOR_BATCH_SIZE=$VECTOR_BATCH_SIZE" >> .env + echo "VECTOR_BATCH_TIMEOUT=$VECTOR_BATCH_TIMEOUT" >> .env + echo "QUERY_NUMBER_OF_SOURCES=$QUERY_NUMBER_OF_SOURCES" >> .env + echo "QUERY_TTL=$QUERY_TTL" >> .env + echo "QUERY_QUEUE_TTL=$QUERY_QUEUE_TTL" >> .env + echo "QUERY_EXPANSION=$QUERY_EXPANSION" >> .env + echo "QUERY_SUMMARY_UPPER_BOUND=$QUERY_SUMMARY_UPPER_BOUND" >> .env + echo "QUERY_SUMMARY_LOWER_BOUND=$QUERY_SUMMARY_LOWER_BOUND" >> .env + echo "QUERY_DEFAULT_MODEL=$QUERY_DEFAULT_MODEL" >> .env + echo "RETRIEVAL_K_VAL=$RETRIEVAL_K_VAL" >> .env echo "DATABASE_URL=$DATABASE_URL" >> .env echo "POSTGRES_USER=$POSTGRES_USER" >> .env echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env echo "POSTGRES_DB=$POSTGRES_DB" >> .env + echo "COUCHDB_USER=$COUCHDB_USER" >> .env + echo "COUCHDB_PASSWORD=$COUCHDB_PASSWORD" >> .env echo "JWT_SECRET=$JWT_SECRET" >> .env echo "ARGON2_MEMORY=$ARGON2_MEMORY" >> .env echo "ARGON2_ITERATIONS=$ARGON2_ITERATIONS" >> .env @@ -166,13 +272,36 @@ jobs: echo "KAFKA_LOG4J_ROOT_LOGLEVEL=$KAFKA_LOG4J_ROOT_LOGLEVEL" >> .env echo "KAFKA_LOG4J_LOGGERS=$KAFKA_LOG4J_LOGGERS" >> .env echo "OLLAMA_URL=$OLLAMA_URL" >> .env + echo "GEMINI_API_KEY=$GEMINI_API_KEY" >> .env + echo "DEEPINFRA_API_KEY=$DEEPINFRA_API_KEY" >> .env + echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env echo "LLM_MODEL=$LLM_MODEL" >> .env - echo "EMBED_URL=$EMBED_URL" >> .env - echo "EMBED_MODEL=$EMBED_MODEL" >> .env echo "ZILLIZ_ADDRESS=$ZILLIZ_ADDRESS" >> .env echo "ZILLIZ_API_KEY=$ZILLIZ_API_KEY" >> .env echo "VECTOR_DIMENSION=$VECTOR_DIMENSION" >> .env + echo "GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID" >> .env + echo "GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET" >> .env + echo "GOOGLE_AUTH_URL=$GOOGLE_AUTH_URL" >> .env + echo "GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI" >> .env + echo "GOOGLE_SCOPES=$GOOGLE_SCOPES" >> .env + echo "GOOGLE_SSO_CLIENT_ID=$GOOGLE_SSO_CLIENT_ID" >> .env + echo "GOOGLE_SSO_CLIENT_SECRET=$GOOGLE_SSO_CLIENT_SECRET" >> .env + echo "GOOGLE_SSO_REDIRECT_URI=$GOOGLE_SSO_REDIRECT_URI" >> .env + echo "GOOGLE_SSO_SCOPES=$GOOGLE_SSO_SCOPES" >> .env + echo "MICROSOFT_CLIENT_ID=$MICROSOFT_CLIENT_ID" >> .env + echo "MICROSOFT_CLIENT_SECRET=$MICROSOFT_CLIENT_SECRET" >> .env + echo "MICROSOFT_AUTH_URL=$MICROSOFT_AUTH_URL" >> .env + echo "MICROSOFT_REDIRECT_URI=$MICROSOFT_REDIRECT_URI" >> .env + echo "MICROSOFT_SCOPES=$MICROSOFT_SCOPES" >> .env + echo "NOTION_CLIENT_ID=$NOTION_CLIENT_ID" >> .env + echo "NOTION_CLIENT_SECRET=$NOTION_CLIENT_SECRET" >> .env + echo "NOTION_REDIRECT_URI=$NOTION_REDIRECT_URI" >> .env + echo "NOTION_AUTH_URL=$NOTION_AUTH_URL" >> .env + echo "REDIS_ADDRESS=$REDIS_ADDRESS" >> .env + echo "REDIS_PASSWORD=$REDIS_PASSWORD" >> .env + echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> .env echo "CA_CRT=$CA_CRT" >> .env + echo "CA_KEY=$CA_KEY" >> .env echo "QUERY_CRT=$QUERY_CRT" >> .env echo "QUERY_KEY=$QUERY_KEY" >> .env echo "AUTH_CRT=$AUTH_CRT" >> .env @@ -183,11 +312,27 @@ jobs: echo "GATEWAY_KEY=$GATEWAY_KEY" >> .env echo "DESKTOP_CRT=$DESKTOP_CRT" >> .env echo "DESKTOP_KEY=$DESKTOP_KEY" >> .env + echo "EMBEDDING_CRT=$EMBEDDING_CRT" >> .env + echo "EMBEDDING_KEY=$EMBEDDING_KEY" >> .env + echo "RETRIEVAL_CRT=$RETRIEVAL_CRT" >> .env + echo "RETRIEVAL_KEY=$RETRIEVAL_KEY" >> .env + echo "MQTT_CRT=$MQTT_CRT" >> .env + echo "MQTT_KEY=$MQTT_KEY" >> .env + echo "WAITLIST_CRT=$WAITLIST_CRT" >> .env + echo "WAITLIST_KEY=$WAITLIST_KEY" >> .env + echo "INTEGRATION_CRT=$INTEGRATION_CRT" >> .env + echo "INTEGRATION_KEY=$INTEGRATION_KEY" >> .env + echo "CRAWLING_CRT=$CRAWLING_CRT" >> .env + echo "CRAWLING_KEY=$CRAWLING_KEY" >> .env echo "ALLOWED_CLIENT_IP=$ALLOWED_CLIENT_IP" >> .env + echo "SMTP_FROM=$SMTP_FROM" >> .env + echo "SMTP_USER=$SMTP_USER" >> .env + echo "SMTP_PASS=$SMTP_PASS" >> .env + echo "SMTP_HOST=$SMTP_HOST" >> .env + echo "SMTP_PORT=$SMTP_PORT" >> .env echo "EC2_KEY_PAIR=$EC2_KEY_PAIR" >> .env echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env - echo "EC2_PUBLIC_HOST=$EC2_PUBLIC_HOST" >> .env - echo "ROOT_CA_CERT=$ROOT_CA_CERT" >> .env + mv .env backend/common/config/.env - name: Generate Code @@ -225,10 +370,51 @@ jobs: docker tag gateway:$IMAGE_TAG $gatewayImage docker push $gatewayImage + docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" + docker tag init:$IMAGE_TAG $initImage + docker push $initImage + + docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" + docker tag embedding:$IMAGE_TAG $embeddingImage + docker push $embeddingImage + + docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" + docker tag retrieval:$IMAGE_TAG $retrievalImage + docker push $retrievalImage + + docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" + docker tag vector:$IMAGE_TAG $vectorImage + docker push $vectorImage + + docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" + docker tag desktop:$IMAGE_TAG $desktopImage + docker push $desktopImage + + docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" + docker tag integration:$IMAGE_TAG $integrationImage + docker push $integrationImage + + docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" + docker tag crawling:$IMAGE_TAG $crawlingImage + docker push $crawlingImage + # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml + sed -i "s|initImage|$initImage|g" backend/docker-compose-ec2.yaml + sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose-ec2.yaml + sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose-ec2.yaml + sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml + sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml + sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml - name: Extract EC2 Key Pair run: | From c3af60e02c975bd718c1f57ccf326c61814a580f Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 00:48:57 -0400 Subject: [PATCH 107/160] fix: debugging ssh connection --- .github/workflows/EC2_deploy.yaml | 734 +++++++++++++++--------------- 1 file changed, 367 insertions(+), 367 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 96f0775b..fd733710 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -47,374 +47,374 @@ jobs: run: | protoc-gen-go --version || echo "protoc-gen-go not found" - - name: Generate .env Files - env: - AUTH_PORT: ${{ secrets.AUTH_PORT }} - DEV_PROD: ${{ secrets.DEV_PROD }} - AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} - QUERY_PORT: ${{ secrets.QUERY_PORT }} - QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} - VECTOR_PORT: ${{ secrets.VECTOR_PORT }} - VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} - DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} - DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} - RETRIEVAL_ADDRESS: ${{ secrets.RETRIEVAL_ADDRESS }} - RETRIEVAL_PORT: ${{ secrets.RETRIEVAL_PORT }} - WAITLIST_ADDRESS: ${{ secrets.WAITLIST_ADDRESS }} - WAITLIST_PORT: ${{ secrets.WAITLIST_PORT }} - INTEGRATION_ADDRESS: ${{ secrets.INTEGRATION_ADDRESS }} - INTEGRATION_PORT: ${{ secrets.INTEGRATION_PORT }} - GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} - CRAWLING_PORT: ${{ secrets.CRAWLING_PORT }} - CRAWLING_ADDRESS: ${{ secrets.CRAWLING_ADDRESS }} - COUCHDB_ADDRESS: ${{ secrets.COUCHDB_ADDRESS }} - MQTT_ADDRESS: ${{ secrets.MQTT_ADDRESS }} - MQTT_PORT: ${{ secrets.MQTT_PORT }} - EMBEDDING_MODEL_NAME: ${{ secrets.EMBEDDING_MODEL_NAME }} - EMBEDDING_RERANKER: ${{ secrets.EMBEDDING_RERANKER }} - EMBEDDING_PORT: ${{ secrets.EMBEDDING_PORT }} - EMBEDDING_ADDRESS: ${{ secrets.EMBEDDING_ADDRESS }} - EMBEDDING_MAX_WORKERS: ${{ secrets.EMBEDDING_MAX_WORKERS }} - EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT: ${{ secrets.EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT }} - CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS }} - CRAWLING_GMAIL_MAX_WORKERS: ${{ secrets.CRAWLING_GMAIL_MAX_WORKERS }} - CRAWLING_DRIVE_MAX_WORKERS: ${{ secrets.CRAWLING_DRIVE_MAX_WORKERS }} - CRAWLING_GOOGLEDOCS_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEDOCS_MAX_WORKERS }} - CRAWLING_GOOGLESLIDES_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLESLIDES_MAX_WORKERS }} - CRAWLING_GOOGLEPDF_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEPDF_MAX_WORKERS }} - CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS }} - VECTOR_BATCH_SIZE: ${{ secrets.VECTOR_BATCH_SIZE }} - VECTOR_BATCH_TIMEOUT: ${{ secrets.VECTOR_BATCH_TIMEOUT }} - QUERY_NUMBER_OF_SOURCES: ${{ secrets.QUERY_NUMBER_OF_SOURCES }} - QUERY_TTL: ${{ secrets.QUERY_TTL }} - QUERY_QUEUE_TTL: ${{ secrets.QUERY_QUEUE_TTL }} - QUERY_EXPANSION: ${{ secrets.QUERY_EXPANSION }} - QUERY_SUMMARY_UPPER_BOUND: ${{ secrets.QUERY_SUMMARY_UPPER_BOUND }} - QUERY_SUMMARY_LOWER_BOUND: ${{ secrets.QUERY_SUMMARY_LOWER_BOUND }} - QUERY_DEFAULT_MODEL: ${{ secrets.QUERY_DEFAULT_MODEL }} - RETRIEVAL_K_VAL: ${{ secrets.RETRIEVAL_K_VAL }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - COUCHDB_USER: ${{ secrets.COUCHDB_USER }} - COUCHDB_PASSWORD: ${{ secrets.COUCHDB_PASSWORD }} - JWT_SECRET: ${{ secrets.JWT_SECRET }} - ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} - ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} - ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} - ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} - ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} - MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} - MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} - MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} - RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} - RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} - RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} - RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} - KAFKA_BROKER_ADDRESS: ${{ secrets.KAFKA_BROKER_ADDRESS }} - KAFKA_BROKER_ID: ${{ secrets.KAFKA_BROKER_ID }} - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${{ secrets.KAFKA_LISTENER_SECURITY_PROTOCOL_MAP }} - KAFKA_ADVERTISED_LISTENERS: ${{ secrets.KAFKA_ADVERTISED_LISTENERS }} - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${{ secrets.KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR }} - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: ${{ secrets.KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS }} - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_MIN_ISR }} - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR }} - KAFKA_PROCESS_ROLES: ${{ secrets.KAFKA_PROCESS_ROLES }} - KAFKA_NODE_ID: ${{ secrets.KAFKA_NODE_ID }} - KAFKA_CONTROLLER_QUORUM_VOTERS: ${{ secrets.KAFKA_CONTROLLER_QUORUM_VOTERS }} - KAFKA_LISTENERS: ${{ secrets.KAFKA_LISTENERS }} - KAFKA_INTER_BROKER_LISTENER_NAME: ${{ secrets.KAFKA_INTER_BROKER_LISTENER_NAME }} - KAFKA_CONTROLLER_LISTENER_NAMES: ${{ secrets.KAFKA_CONTROLLER_LISTENER_NAMES }} - CLUSTER_ID: ${{ secrets.CLUSTER_ID }} - KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} - KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} - OLLAMA_URL: ${{ secrets.OLLAMA_URL }} - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - LLM_MODEL: ${{ secrets.LLM_MODEL }} - ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} - ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} - VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} - GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} - GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} - GOOGLE_AUTH_URL: ${{ secrets.GOOGLE_AUTH_URL }} - GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} - GOOGLE_SCOPES: ${{ secrets.GOOGLE_SCOPES }} - GOOGLE_SSO_CLIENT_ID: ${{ secrets.GOOGLE_SSO_CLIENT_ID }} - GOOGLE_SSO_CLIENT_SECRET: ${{ secrets.GOOGLE_SSO_CLIENT_SECRET }} - GOOGLE_SSO_REDIRECT_URI: ${{ secrets.GOOGLE_SSO_REDIRECT_URI }} - GOOGLE_SSO_SCOPES: ${{ secrets.GOOGLE_SSO_SCOPES }} - MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} - MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} - MICROSOFT_AUTH_URL: ${{ secrets.MICROSOFT_AUTH_URL }} - MICROSOFT_REDIRECT_URI: ${{ secrets.MICROSOFT_REDIRECT_URI }} - MICROSOFT_SCOPES: ${{ secrets.MICROSOFT_SCOPES }} - NOTION_CLIENT_ID: ${{ secrets.NOTION_CLIENT_ID }} - NOTION_CLIENT_SECRET: ${{ secrets.NOTION_CLIENT_SECRET }} - NOTION_REDIRECT_URI: ${{ secrets.NOTION_REDIRECT_URI }} - NOTION_AUTH_URL: ${{ secrets.NOTION_AUTH_URL }} - REDIS_ADDRESS: ${{ secrets.REDIS_ADDRESS }} - REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} - ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} - CA_CRT: ${{ secrets.CA_CRT }} - CA_KEY: ${{ secrets.CA_KEY }} - QUERY_CRT: ${{ secrets.QUERY_CRT }} - QUERY_KEY: ${{ secrets.QUERY_KEY }} - AUTH_CRT: ${{ secrets.AUTH_CRT }} - AUTH_KEY: ${{ secrets.AUTH_KEY }} - VECTOR_CRT: ${{ secrets.VECTOR_CRT }} - VECTOR_KEY: ${{ secrets.VECTOR_KEY }} - GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} - GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} - DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} - DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} - EMBEDDING_CRT: ${{ secrets.EMBEDDING_CRT }} - EMBEDDING_KEY: ${{ secrets.EMBEDDING_KEY }} - RETRIEVAL_CRT: ${{ secrets.RETRIEVAL_CRT }} - RETRIEVAL_KEY: ${{ secrets.RETRIEVAL_KEY }} - MQTT_CRT: ${{ secrets.MQTT_CRT }} - MQTT_KEY: ${{ secrets.MQTT_KEY }} - WAITLIST_CRT: ${{ secrets.WAITLIST_CRT }} - WAITLIST_KEY: ${{ secrets.WAITLIST_KEY }} - INTEGRATION_CRT: ${{ secrets.INTEGRATION_CRT }} - INTEGRATION_KEY: ${{ secrets.INTEGRATION_KEY }} - CRAWLING_CRT: ${{ secrets.CRAWLING_CRT }} - CRAWLING_KEY: ${{ secrets.CRAWLING_KEY }} - ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} - SMTP_FROM: ${{ secrets.SMTP_FROM }} - SMTP_USER: ${{ secrets.SMTP_USER }} - SMTP_PASS: ${{ secrets.SMTP_PASS }} - SMTP_HOST: ${{ secrets.SMTP_HOST }} - SMTP_PORT: ${{ secrets.SMTP_PORT }} - EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} - EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} - run: | - echo "AUTH_PORT=$AUTH_PORT" > .env - echo "DEV_PROD=$DEV_PROD" >> .env - echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> .env - echo "QUERY_PORT=$QUERY_PORT" >> .env - echo "QUERY_ADDRESS=$QUERY_ADDRESS" >> .env - echo "VECTOR_PORT=$VECTOR_PORT" >> .env - echo "VECTOR_ADDRESS=$VECTOR_ADDRESS" >> .env - echo "DESKTOP_ADDRESS=$DESKTOP_ADDRESS" >> .env - echo "DESKTOP_PORT=$DESKTOP_PORT" >> .env - echo "RETRIEVAL_ADDRESS=$RETRIEVAL_ADDRESS" >> .env - echo "RETRIEVAL_PORT=$RETRIEVAL_PORT" >> .env - echo "WAITLIST_ADDRESS=$WAITLIST_ADDRESS" >> .env - echo "WAITLIST_PORT=$WAITLIST_PORT" >> .env - echo "INTEGRATION_ADDRESS=$INTEGRATION_ADDRESS" >> .env - echo "INTEGRATION_PORT=$INTEGRATION_PORT" >> .env - echo "GATEWAY_ADDRESS=$GATEWAY_ADDRESS" >> .env - echo "CRAWLING_PORT=$CRAWLING_PORT" >> .env - echo "CRAWLING_ADDRESS=$CRAWLING_ADDRESS" >> .env - echo "COUCHDB_ADDRESS=$COUCHDB_ADDRESS" >> .env - echo "MQTT_ADDRESS=$MQTT_ADDRESS" >> .env - echo "MQTT_PORT=$MQTT_PORT" >> .env - echo "EMBEDDING_MODEL_NAME=$EMBEDDING_MODEL_NAME" >> .env - echo "EMBEDDING_RERANKER=$EMBEDDING_RERANKER" >> .env - echo "EMBEDDING_PORT=$EMBEDDING_PORT" >> .env - echo "EMBEDDING_ADDRESS=$EMBEDDING_ADDRESS" >> .env - echo "EMBEDDING_MAX_WORKERS=$EMBEDDING_MAX_WORKERS" >> .env - echo "EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT=$EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT" >> .env - echo "CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS=$CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS" >> .env - echo "CRAWLING_GMAIL_MAX_WORKERS=$CRAWLING_GMAIL_MAX_WORKERS" >> .env - echo "CRAWLING_DRIVE_MAX_WORKERS=$CRAWLING_DRIVE_MAX_WORKERS" >> .env - echo "CRAWLING_GOOGLEDOCS_MAX_WORKERS=$CRAWLING_GOOGLEDOCS_MAX_WORKERS" >> .env - echo "CRAWLING_GOOGLESLIDES_MAX_WORKERS=$CRAWLING_GOOGLESLIDES_MAX_WORKERS" >> .env - echo "CRAWLING_GOOGLEPDF_MAX_WORKERS=$CRAWLING_GOOGLEPDF_MAX_WORKERS" >> .env - echo "CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS=$CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS" >> .env - echo "VECTOR_BATCH_SIZE=$VECTOR_BATCH_SIZE" >> .env - echo "VECTOR_BATCH_TIMEOUT=$VECTOR_BATCH_TIMEOUT" >> .env - echo "QUERY_NUMBER_OF_SOURCES=$QUERY_NUMBER_OF_SOURCES" >> .env - echo "QUERY_TTL=$QUERY_TTL" >> .env - echo "QUERY_QUEUE_TTL=$QUERY_QUEUE_TTL" >> .env - echo "QUERY_EXPANSION=$QUERY_EXPANSION" >> .env - echo "QUERY_SUMMARY_UPPER_BOUND=$QUERY_SUMMARY_UPPER_BOUND" >> .env - echo "QUERY_SUMMARY_LOWER_BOUND=$QUERY_SUMMARY_LOWER_BOUND" >> .env - echo "QUERY_DEFAULT_MODEL=$QUERY_DEFAULT_MODEL" >> .env - echo "RETRIEVAL_K_VAL=$RETRIEVAL_K_VAL" >> .env - echo "DATABASE_URL=$DATABASE_URL" >> .env - echo "POSTGRES_USER=$POSTGRES_USER" >> .env - echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env - echo "POSTGRES_DB=$POSTGRES_DB" >> .env - echo "COUCHDB_USER=$COUCHDB_USER" >> .env - echo "COUCHDB_PASSWORD=$COUCHDB_PASSWORD" >> .env - echo "JWT_SECRET=$JWT_SECRET" >> .env - echo "ARGON2_MEMORY=$ARGON2_MEMORY" >> .env - echo "ARGON2_ITERATIONS=$ARGON2_ITERATIONS" >> .env - echo "ARGON2_PARALLELISM=$ARGON2_PARALLELISM" >> .env - echo "ARGON2_SALT_LENGTH=$ARGON2_SALT_LENGTH" >> .env - echo "ARGON2_KEY_LENGTH=$ARGON2_KEY_LENGTH" >> .env - echo "MIN_PASSWORD_LENGTH=$MIN_PASSWORD_LENGTH" >> .env - echo "MAX_PASSWORD_LENGTH=$MAX_PASSWORD_LENGTH" >> .env - echo "MAX_EMAIL_LENGTH=$MAX_EMAIL_LENGTH" >> .env - echo "RABBITMQ_URL=$RABBITMQ_URL" >> .env - echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> .env - echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> .env - echo "RABBITMQ_LOGS=$RABBITMQ_LOGS" >> .env - echo "KAFKA_BROKER_ADDRESS=$KAFKA_BROKER_ADDRESS" >> .env - echo "KAFKA_BROKER_ID=$KAFKA_BROKER_ID" >> .env - echo "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=$KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" >> .env - echo "KAFKA_ADVERTISED_LISTENERS=$KAFKA_ADVERTISED_LISTENERS" >> .env - echo "KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=$KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" >> .env - echo "KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=$KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" >> .env - echo "KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=$KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" >> .env - echo "KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=$KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" >> .env - echo "KAFKA_PROCESS_ROLES=$KAFKA_PROCESS_ROLES" >> .env - echo "KAFKA_NODE_ID=$KAFKA_NODE_ID" >> .env - echo "KAFKA_CONTROLLER_QUORUM_VOTERS=$KAFKA_CONTROLLER_QUORUM_VOTERS" >> .env - echo "KAFKA_LISTENERS=$KAFKA_LISTENERS" >> .env - echo "KAFKA_INTER_BROKER_LISTENER_NAME=$KAFKA_INTER_BROKER_LISTENER_NAME" >> .env - echo "KAFKA_CONTROLLER_LISTENER_NAMES=$KAFKA_CONTROLLER_LISTENER_NAMES" >> .env - echo "CLUSTER_ID=$CLUSTER_ID" >> .env - echo "KAFKA_LOG4J_ROOT_LOGLEVEL=$KAFKA_LOG4J_ROOT_LOGLEVEL" >> .env - echo "KAFKA_LOG4J_LOGGERS=$KAFKA_LOG4J_LOGGERS" >> .env - echo "OLLAMA_URL=$OLLAMA_URL" >> .env - echo "GEMINI_API_KEY=$GEMINI_API_KEY" >> .env - echo "DEEPINFRA_API_KEY=$DEEPINFRA_API_KEY" >> .env - echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env - echo "LLM_MODEL=$LLM_MODEL" >> .env - echo "ZILLIZ_ADDRESS=$ZILLIZ_ADDRESS" >> .env - echo "ZILLIZ_API_KEY=$ZILLIZ_API_KEY" >> .env - echo "VECTOR_DIMENSION=$VECTOR_DIMENSION" >> .env - echo "GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID" >> .env - echo "GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET" >> .env - echo "GOOGLE_AUTH_URL=$GOOGLE_AUTH_URL" >> .env - echo "GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI" >> .env - echo "GOOGLE_SCOPES=$GOOGLE_SCOPES" >> .env - echo "GOOGLE_SSO_CLIENT_ID=$GOOGLE_SSO_CLIENT_ID" >> .env - echo "GOOGLE_SSO_CLIENT_SECRET=$GOOGLE_SSO_CLIENT_SECRET" >> .env - echo "GOOGLE_SSO_REDIRECT_URI=$GOOGLE_SSO_REDIRECT_URI" >> .env - echo "GOOGLE_SSO_SCOPES=$GOOGLE_SSO_SCOPES" >> .env - echo "MICROSOFT_CLIENT_ID=$MICROSOFT_CLIENT_ID" >> .env - echo "MICROSOFT_CLIENT_SECRET=$MICROSOFT_CLIENT_SECRET" >> .env - echo "MICROSOFT_AUTH_URL=$MICROSOFT_AUTH_URL" >> .env - echo "MICROSOFT_REDIRECT_URI=$MICROSOFT_REDIRECT_URI" >> .env - echo "MICROSOFT_SCOPES=$MICROSOFT_SCOPES" >> .env - echo "NOTION_CLIENT_ID=$NOTION_CLIENT_ID" >> .env - echo "NOTION_CLIENT_SECRET=$NOTION_CLIENT_SECRET" >> .env - echo "NOTION_REDIRECT_URI=$NOTION_REDIRECT_URI" >> .env - echo "NOTION_AUTH_URL=$NOTION_AUTH_URL" >> .env - echo "REDIS_ADDRESS=$REDIS_ADDRESS" >> .env - echo "REDIS_PASSWORD=$REDIS_PASSWORD" >> .env - echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> .env - echo "CA_CRT=$CA_CRT" >> .env - echo "CA_KEY=$CA_KEY" >> .env - echo "QUERY_CRT=$QUERY_CRT" >> .env - echo "QUERY_KEY=$QUERY_KEY" >> .env - echo "AUTH_CRT=$AUTH_CRT" >> .env - echo "AUTH_KEY=$AUTH_KEY" >> .env - echo "VECTOR_CRT=$VECTOR_CRT" >> .env - echo "VECTOR_KEY=$VECTOR_KEY" >> .env - echo "GATEWAY_CRT=$GATEWAY_CRT" >> .env - echo "GATEWAY_KEY=$GATEWAY_KEY" >> .env - echo "DESKTOP_CRT=$DESKTOP_CRT" >> .env - echo "DESKTOP_KEY=$DESKTOP_KEY" >> .env - echo "EMBEDDING_CRT=$EMBEDDING_CRT" >> .env - echo "EMBEDDING_KEY=$EMBEDDING_KEY" >> .env - echo "RETRIEVAL_CRT=$RETRIEVAL_CRT" >> .env - echo "RETRIEVAL_KEY=$RETRIEVAL_KEY" >> .env - echo "MQTT_CRT=$MQTT_CRT" >> .env - echo "MQTT_KEY=$MQTT_KEY" >> .env - echo "WAITLIST_CRT=$WAITLIST_CRT" >> .env - echo "WAITLIST_KEY=$WAITLIST_KEY" >> .env - echo "INTEGRATION_CRT=$INTEGRATION_CRT" >> .env - echo "INTEGRATION_KEY=$INTEGRATION_KEY" >> .env - echo "CRAWLING_CRT=$CRAWLING_CRT" >> .env - echo "CRAWLING_KEY=$CRAWLING_KEY" >> .env - echo "ALLOWED_CLIENT_IP=$ALLOWED_CLIENT_IP" >> .env - echo "SMTP_FROM=$SMTP_FROM" >> .env - echo "SMTP_USER=$SMTP_USER" >> .env - echo "SMTP_PASS=$SMTP_PASS" >> .env - echo "SMTP_HOST=$SMTP_HOST" >> .env - echo "SMTP_PORT=$SMTP_PORT" >> .env - echo "EC2_KEY_PAIR=$EC2_KEY_PAIR" >> .env - echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env + # - name: Generate .env Files + # env: + # AUTH_PORT: ${{ secrets.AUTH_PORT }} + # DEV_PROD: ${{ secrets.DEV_PROD }} + # AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + # QUERY_PORT: ${{ secrets.QUERY_PORT }} + # QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + # VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + # VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + # DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} + # DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} + # RETRIEVAL_ADDRESS: ${{ secrets.RETRIEVAL_ADDRESS }} + # RETRIEVAL_PORT: ${{ secrets.RETRIEVAL_PORT }} + # WAITLIST_ADDRESS: ${{ secrets.WAITLIST_ADDRESS }} + # WAITLIST_PORT: ${{ secrets.WAITLIST_PORT }} + # INTEGRATION_ADDRESS: ${{ secrets.INTEGRATION_ADDRESS }} + # INTEGRATION_PORT: ${{ secrets.INTEGRATION_PORT }} + # GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + # CRAWLING_PORT: ${{ secrets.CRAWLING_PORT }} + # CRAWLING_ADDRESS: ${{ secrets.CRAWLING_ADDRESS }} + # COUCHDB_ADDRESS: ${{ secrets.COUCHDB_ADDRESS }} + # MQTT_ADDRESS: ${{ secrets.MQTT_ADDRESS }} + # MQTT_PORT: ${{ secrets.MQTT_PORT }} + # EMBEDDING_MODEL_NAME: ${{ secrets.EMBEDDING_MODEL_NAME }} + # EMBEDDING_RERANKER: ${{ secrets.EMBEDDING_RERANKER }} + # EMBEDDING_PORT: ${{ secrets.EMBEDDING_PORT }} + # EMBEDDING_ADDRESS: ${{ secrets.EMBEDDING_ADDRESS }} + # EMBEDDING_MAX_WORKERS: ${{ secrets.EMBEDDING_MAX_WORKERS }} + # EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT: ${{ secrets.EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT }} + # CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS }} + # CRAWLING_GMAIL_MAX_WORKERS: ${{ secrets.CRAWLING_GMAIL_MAX_WORKERS }} + # CRAWLING_DRIVE_MAX_WORKERS: ${{ secrets.CRAWLING_DRIVE_MAX_WORKERS }} + # CRAWLING_GOOGLEDOCS_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEDOCS_MAX_WORKERS }} + # CRAWLING_GOOGLESLIDES_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLESLIDES_MAX_WORKERS }} + # CRAWLING_GOOGLEPDF_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEPDF_MAX_WORKERS }} + # CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS }} + # VECTOR_BATCH_SIZE: ${{ secrets.VECTOR_BATCH_SIZE }} + # VECTOR_BATCH_TIMEOUT: ${{ secrets.VECTOR_BATCH_TIMEOUT }} + # QUERY_NUMBER_OF_SOURCES: ${{ secrets.QUERY_NUMBER_OF_SOURCES }} + # QUERY_TTL: ${{ secrets.QUERY_TTL }} + # QUERY_QUEUE_TTL: ${{ secrets.QUERY_QUEUE_TTL }} + # QUERY_EXPANSION: ${{ secrets.QUERY_EXPANSION }} + # QUERY_SUMMARY_UPPER_BOUND: ${{ secrets.QUERY_SUMMARY_UPPER_BOUND }} + # QUERY_SUMMARY_LOWER_BOUND: ${{ secrets.QUERY_SUMMARY_LOWER_BOUND }} + # QUERY_DEFAULT_MODEL: ${{ secrets.QUERY_DEFAULT_MODEL }} + # RETRIEVAL_K_VAL: ${{ secrets.RETRIEVAL_K_VAL }} + # DATABASE_URL: ${{ secrets.DATABASE_URL }} + # POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + # POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + # POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + # COUCHDB_USER: ${{ secrets.COUCHDB_USER }} + # COUCHDB_PASSWORD: ${{ secrets.COUCHDB_PASSWORD }} + # JWT_SECRET: ${{ secrets.JWT_SECRET }} + # ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + # ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + # ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + # ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + # ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + # MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + # MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + # MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + # RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + # RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + # RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + # RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + # KAFKA_BROKER_ADDRESS: ${{ secrets.KAFKA_BROKER_ADDRESS }} + # KAFKA_BROKER_ID: ${{ secrets.KAFKA_BROKER_ID }} + # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${{ secrets.KAFKA_LISTENER_SECURITY_PROTOCOL_MAP }} + # KAFKA_ADVERTISED_LISTENERS: ${{ secrets.KAFKA_ADVERTISED_LISTENERS }} + # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${{ secrets.KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR }} + # KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: ${{ secrets.KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS }} + # KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_MIN_ISR }} + # KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR }} + # KAFKA_PROCESS_ROLES: ${{ secrets.KAFKA_PROCESS_ROLES }} + # KAFKA_NODE_ID: ${{ secrets.KAFKA_NODE_ID }} + # KAFKA_CONTROLLER_QUORUM_VOTERS: ${{ secrets.KAFKA_CONTROLLER_QUORUM_VOTERS }} + # KAFKA_LISTENERS: ${{ secrets.KAFKA_LISTENERS }} + # KAFKA_INTER_BROKER_LISTENER_NAME: ${{ secrets.KAFKA_INTER_BROKER_LISTENER_NAME }} + # KAFKA_CONTROLLER_LISTENER_NAMES: ${{ secrets.KAFKA_CONTROLLER_LISTENER_NAMES }} + # CLUSTER_ID: ${{ secrets.CLUSTER_ID }} + # KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} + # KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} + # OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + # GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + # DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }} + # OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + # LLM_MODEL: ${{ secrets.LLM_MODEL }} + # ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + # ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + # VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} + # GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} + # GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} + # GOOGLE_AUTH_URL: ${{ secrets.GOOGLE_AUTH_URL }} + # GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} + # GOOGLE_SCOPES: ${{ secrets.GOOGLE_SCOPES }} + # GOOGLE_SSO_CLIENT_ID: ${{ secrets.GOOGLE_SSO_CLIENT_ID }} + # GOOGLE_SSO_CLIENT_SECRET: ${{ secrets.GOOGLE_SSO_CLIENT_SECRET }} + # GOOGLE_SSO_REDIRECT_URI: ${{ secrets.GOOGLE_SSO_REDIRECT_URI }} + # GOOGLE_SSO_SCOPES: ${{ secrets.GOOGLE_SSO_SCOPES }} + # MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} + # MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} + # MICROSOFT_AUTH_URL: ${{ secrets.MICROSOFT_AUTH_URL }} + # MICROSOFT_REDIRECT_URI: ${{ secrets.MICROSOFT_REDIRECT_URI }} + # MICROSOFT_SCOPES: ${{ secrets.MICROSOFT_SCOPES }} + # NOTION_CLIENT_ID: ${{ secrets.NOTION_CLIENT_ID }} + # NOTION_CLIENT_SECRET: ${{ secrets.NOTION_CLIENT_SECRET }} + # NOTION_REDIRECT_URI: ${{ secrets.NOTION_REDIRECT_URI }} + # NOTION_AUTH_URL: ${{ secrets.NOTION_AUTH_URL }} + # REDIS_ADDRESS: ${{ secrets.REDIS_ADDRESS }} + # REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} + # ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} + # CA_CRT: ${{ secrets.CA_CRT }} + # CA_KEY: ${{ secrets.CA_KEY }} + # QUERY_CRT: ${{ secrets.QUERY_CRT }} + # QUERY_KEY: ${{ secrets.QUERY_KEY }} + # AUTH_CRT: ${{ secrets.AUTH_CRT }} + # AUTH_KEY: ${{ secrets.AUTH_KEY }} + # VECTOR_CRT: ${{ secrets.VECTOR_CRT }} + # VECTOR_KEY: ${{ secrets.VECTOR_KEY }} + # GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + # GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + # DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} + # DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} + # EMBEDDING_CRT: ${{ secrets.EMBEDDING_CRT }} + # EMBEDDING_KEY: ${{ secrets.EMBEDDING_KEY }} + # RETRIEVAL_CRT: ${{ secrets.RETRIEVAL_CRT }} + # RETRIEVAL_KEY: ${{ secrets.RETRIEVAL_KEY }} + # MQTT_CRT: ${{ secrets.MQTT_CRT }} + # MQTT_KEY: ${{ secrets.MQTT_KEY }} + # WAITLIST_CRT: ${{ secrets.WAITLIST_CRT }} + # WAITLIST_KEY: ${{ secrets.WAITLIST_KEY }} + # INTEGRATION_CRT: ${{ secrets.INTEGRATION_CRT }} + # INTEGRATION_KEY: ${{ secrets.INTEGRATION_KEY }} + # CRAWLING_CRT: ${{ secrets.CRAWLING_CRT }} + # CRAWLING_KEY: ${{ secrets.CRAWLING_KEY }} + # ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + # SMTP_FROM: ${{ secrets.SMTP_FROM }} + # SMTP_USER: ${{ secrets.SMTP_USER }} + # SMTP_PASS: ${{ secrets.SMTP_PASS }} + # SMTP_HOST: ${{ secrets.SMTP_HOST }} + # SMTP_PORT: ${{ secrets.SMTP_PORT }} + # EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} + # EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} + # run: | + # echo "AUTH_PORT=$AUTH_PORT" > .env + # echo "DEV_PROD=$DEV_PROD" >> .env + # echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> .env + # echo "QUERY_PORT=$QUERY_PORT" >> .env + # echo "QUERY_ADDRESS=$QUERY_ADDRESS" >> .env + # echo "VECTOR_PORT=$VECTOR_PORT" >> .env + # echo "VECTOR_ADDRESS=$VECTOR_ADDRESS" >> .env + # echo "DESKTOP_ADDRESS=$DESKTOP_ADDRESS" >> .env + # echo "DESKTOP_PORT=$DESKTOP_PORT" >> .env + # echo "RETRIEVAL_ADDRESS=$RETRIEVAL_ADDRESS" >> .env + # echo "RETRIEVAL_PORT=$RETRIEVAL_PORT" >> .env + # echo "WAITLIST_ADDRESS=$WAITLIST_ADDRESS" >> .env + # echo "WAITLIST_PORT=$WAITLIST_PORT" >> .env + # echo "INTEGRATION_ADDRESS=$INTEGRATION_ADDRESS" >> .env + # echo "INTEGRATION_PORT=$INTEGRATION_PORT" >> .env + # echo "GATEWAY_ADDRESS=$GATEWAY_ADDRESS" >> .env + # echo "CRAWLING_PORT=$CRAWLING_PORT" >> .env + # echo "CRAWLING_ADDRESS=$CRAWLING_ADDRESS" >> .env + # echo "COUCHDB_ADDRESS=$COUCHDB_ADDRESS" >> .env + # echo "MQTT_ADDRESS=$MQTT_ADDRESS" >> .env + # echo "MQTT_PORT=$MQTT_PORT" >> .env + # echo "EMBEDDING_MODEL_NAME=$EMBEDDING_MODEL_NAME" >> .env + # echo "EMBEDDING_RERANKER=$EMBEDDING_RERANKER" >> .env + # echo "EMBEDDING_PORT=$EMBEDDING_PORT" >> .env + # echo "EMBEDDING_ADDRESS=$EMBEDDING_ADDRESS" >> .env + # echo "EMBEDDING_MAX_WORKERS=$EMBEDDING_MAX_WORKERS" >> .env + # echo "EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT=$EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT" >> .env + # echo "CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS=$CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS" >> .env + # echo "CRAWLING_GMAIL_MAX_WORKERS=$CRAWLING_GMAIL_MAX_WORKERS" >> .env + # echo "CRAWLING_DRIVE_MAX_WORKERS=$CRAWLING_DRIVE_MAX_WORKERS" >> .env + # echo "CRAWLING_GOOGLEDOCS_MAX_WORKERS=$CRAWLING_GOOGLEDOCS_MAX_WORKERS" >> .env + # echo "CRAWLING_GOOGLESLIDES_MAX_WORKERS=$CRAWLING_GOOGLESLIDES_MAX_WORKERS" >> .env + # echo "CRAWLING_GOOGLEPDF_MAX_WORKERS=$CRAWLING_GOOGLEPDF_MAX_WORKERS" >> .env + # echo "CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS=$CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS" >> .env + # echo "VECTOR_BATCH_SIZE=$VECTOR_BATCH_SIZE" >> .env + # echo "VECTOR_BATCH_TIMEOUT=$VECTOR_BATCH_TIMEOUT" >> .env + # echo "QUERY_NUMBER_OF_SOURCES=$QUERY_NUMBER_OF_SOURCES" >> .env + # echo "QUERY_TTL=$QUERY_TTL" >> .env + # echo "QUERY_QUEUE_TTL=$QUERY_QUEUE_TTL" >> .env + # echo "QUERY_EXPANSION=$QUERY_EXPANSION" >> .env + # echo "QUERY_SUMMARY_UPPER_BOUND=$QUERY_SUMMARY_UPPER_BOUND" >> .env + # echo "QUERY_SUMMARY_LOWER_BOUND=$QUERY_SUMMARY_LOWER_BOUND" >> .env + # echo "QUERY_DEFAULT_MODEL=$QUERY_DEFAULT_MODEL" >> .env + # echo "RETRIEVAL_K_VAL=$RETRIEVAL_K_VAL" >> .env + # echo "DATABASE_URL=$DATABASE_URL" >> .env + # echo "POSTGRES_USER=$POSTGRES_USER" >> .env + # echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env + # echo "POSTGRES_DB=$POSTGRES_DB" >> .env + # echo "COUCHDB_USER=$COUCHDB_USER" >> .env + # echo "COUCHDB_PASSWORD=$COUCHDB_PASSWORD" >> .env + # echo "JWT_SECRET=$JWT_SECRET" >> .env + # echo "ARGON2_MEMORY=$ARGON2_MEMORY" >> .env + # echo "ARGON2_ITERATIONS=$ARGON2_ITERATIONS" >> .env + # echo "ARGON2_PARALLELISM=$ARGON2_PARALLELISM" >> .env + # echo "ARGON2_SALT_LENGTH=$ARGON2_SALT_LENGTH" >> .env + # echo "ARGON2_KEY_LENGTH=$ARGON2_KEY_LENGTH" >> .env + # echo "MIN_PASSWORD_LENGTH=$MIN_PASSWORD_LENGTH" >> .env + # echo "MAX_PASSWORD_LENGTH=$MAX_PASSWORD_LENGTH" >> .env + # echo "MAX_EMAIL_LENGTH=$MAX_EMAIL_LENGTH" >> .env + # echo "RABBITMQ_URL=$RABBITMQ_URL" >> .env + # echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> .env + # echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> .env + # echo "RABBITMQ_LOGS=$RABBITMQ_LOGS" >> .env + # echo "KAFKA_BROKER_ADDRESS=$KAFKA_BROKER_ADDRESS" >> .env + # echo "KAFKA_BROKER_ID=$KAFKA_BROKER_ID" >> .env + # echo "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=$KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" >> .env + # echo "KAFKA_ADVERTISED_LISTENERS=$KAFKA_ADVERTISED_LISTENERS" >> .env + # echo "KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=$KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" >> .env + # echo "KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=$KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" >> .env + # echo "KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=$KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" >> .env + # echo "KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=$KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" >> .env + # echo "KAFKA_PROCESS_ROLES=$KAFKA_PROCESS_ROLES" >> .env + # echo "KAFKA_NODE_ID=$KAFKA_NODE_ID" >> .env + # echo "KAFKA_CONTROLLER_QUORUM_VOTERS=$KAFKA_CONTROLLER_QUORUM_VOTERS" >> .env + # echo "KAFKA_LISTENERS=$KAFKA_LISTENERS" >> .env + # echo "KAFKA_INTER_BROKER_LISTENER_NAME=$KAFKA_INTER_BROKER_LISTENER_NAME" >> .env + # echo "KAFKA_CONTROLLER_LISTENER_NAMES=$KAFKA_CONTROLLER_LISTENER_NAMES" >> .env + # echo "CLUSTER_ID=$CLUSTER_ID" >> .env + # echo "KAFKA_LOG4J_ROOT_LOGLEVEL=$KAFKA_LOG4J_ROOT_LOGLEVEL" >> .env + # echo "KAFKA_LOG4J_LOGGERS=$KAFKA_LOG4J_LOGGERS" >> .env + # echo "OLLAMA_URL=$OLLAMA_URL" >> .env + # echo "GEMINI_API_KEY=$GEMINI_API_KEY" >> .env + # echo "DEEPINFRA_API_KEY=$DEEPINFRA_API_KEY" >> .env + # echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env + # echo "LLM_MODEL=$LLM_MODEL" >> .env + # echo "ZILLIZ_ADDRESS=$ZILLIZ_ADDRESS" >> .env + # echo "ZILLIZ_API_KEY=$ZILLIZ_API_KEY" >> .env + # echo "VECTOR_DIMENSION=$VECTOR_DIMENSION" >> .env + # echo "GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID" >> .env + # echo "GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET" >> .env + # echo "GOOGLE_AUTH_URL=$GOOGLE_AUTH_URL" >> .env + # echo "GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI" >> .env + # echo "GOOGLE_SCOPES=$GOOGLE_SCOPES" >> .env + # echo "GOOGLE_SSO_CLIENT_ID=$GOOGLE_SSO_CLIENT_ID" >> .env + # echo "GOOGLE_SSO_CLIENT_SECRET=$GOOGLE_SSO_CLIENT_SECRET" >> .env + # echo "GOOGLE_SSO_REDIRECT_URI=$GOOGLE_SSO_REDIRECT_URI" >> .env + # echo "GOOGLE_SSO_SCOPES=$GOOGLE_SSO_SCOPES" >> .env + # echo "MICROSOFT_CLIENT_ID=$MICROSOFT_CLIENT_ID" >> .env + # echo "MICROSOFT_CLIENT_SECRET=$MICROSOFT_CLIENT_SECRET" >> .env + # echo "MICROSOFT_AUTH_URL=$MICROSOFT_AUTH_URL" >> .env + # echo "MICROSOFT_REDIRECT_URI=$MICROSOFT_REDIRECT_URI" >> .env + # echo "MICROSOFT_SCOPES=$MICROSOFT_SCOPES" >> .env + # echo "NOTION_CLIENT_ID=$NOTION_CLIENT_ID" >> .env + # echo "NOTION_CLIENT_SECRET=$NOTION_CLIENT_SECRET" >> .env + # echo "NOTION_REDIRECT_URI=$NOTION_REDIRECT_URI" >> .env + # echo "NOTION_AUTH_URL=$NOTION_AUTH_URL" >> .env + # echo "REDIS_ADDRESS=$REDIS_ADDRESS" >> .env + # echo "REDIS_PASSWORD=$REDIS_PASSWORD" >> .env + # echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> .env + # echo "CA_CRT=$CA_CRT" >> .env + # echo "CA_KEY=$CA_KEY" >> .env + # echo "QUERY_CRT=$QUERY_CRT" >> .env + # echo "QUERY_KEY=$QUERY_KEY" >> .env + # echo "AUTH_CRT=$AUTH_CRT" >> .env + # echo "AUTH_KEY=$AUTH_KEY" >> .env + # echo "VECTOR_CRT=$VECTOR_CRT" >> .env + # echo "VECTOR_KEY=$VECTOR_KEY" >> .env + # echo "GATEWAY_CRT=$GATEWAY_CRT" >> .env + # echo "GATEWAY_KEY=$GATEWAY_KEY" >> .env + # echo "DESKTOP_CRT=$DESKTOP_CRT" >> .env + # echo "DESKTOP_KEY=$DESKTOP_KEY" >> .env + # echo "EMBEDDING_CRT=$EMBEDDING_CRT" >> .env + # echo "EMBEDDING_KEY=$EMBEDDING_KEY" >> .env + # echo "RETRIEVAL_CRT=$RETRIEVAL_CRT" >> .env + # echo "RETRIEVAL_KEY=$RETRIEVAL_KEY" >> .env + # echo "MQTT_CRT=$MQTT_CRT" >> .env + # echo "MQTT_KEY=$MQTT_KEY" >> .env + # echo "WAITLIST_CRT=$WAITLIST_CRT" >> .env + # echo "WAITLIST_KEY=$WAITLIST_KEY" >> .env + # echo "INTEGRATION_CRT=$INTEGRATION_CRT" >> .env + # echo "INTEGRATION_KEY=$INTEGRATION_KEY" >> .env + # echo "CRAWLING_CRT=$CRAWLING_CRT" >> .env + # echo "CRAWLING_KEY=$CRAWLING_KEY" >> .env + # echo "ALLOWED_CLIENT_IP=$ALLOWED_CLIENT_IP" >> .env + # echo "SMTP_FROM=$SMTP_FROM" >> .env + # echo "SMTP_USER=$SMTP_USER" >> .env + # echo "SMTP_PASS=$SMTP_PASS" >> .env + # echo "SMTP_HOST=$SMTP_HOST" >> .env + # echo "SMTP_PORT=$SMTP_PORT" >> .env + # echo "EC2_KEY_PAIR=$EC2_KEY_PAIR" >> .env + # echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env - mv .env backend/common/config/.env - - - name: Generate Code - run: | - cd backend/common && make gen - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION }} - - - name: Login to Amazon ECR - run: | - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - - - name: Build and Push Docker Images - run: | - docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" - docker tag authentication:$IMAGE_TAG $authImage - docker push $authImage - - docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" - docker tag query:$IMAGE_TAG $queryImage - docker push $queryImage - - docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" - docker tag gateway:$IMAGE_TAG $gatewayImage - docker push $gatewayImage - - docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend - initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" - docker tag init:$IMAGE_TAG $initImage - docker push $initImage - - docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend - embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" - docker tag embedding:$IMAGE_TAG $embeddingImage - docker push $embeddingImage + # mv .env backend/common/config/.env + + # - name: Generate Code + # run: | + # cd backend/common && make gen + + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v1 + + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v2 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: ${{ env.AWS_REGION }} + + # - name: Login to Amazon ECR + # run: | + # aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + + # - name: Build and Push Docker Images + # run: | + # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + # authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" + # docker tag authentication:$IMAGE_TAG $authImage + # docker push $authImage + + # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + # queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" + # docker tag query:$IMAGE_TAG $queryImage + # docker push $queryImage + + # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + # gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" + # docker tag gateway:$IMAGE_TAG $gatewayImage + # docker push $gatewayImage + + # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + # initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" + # docker tag init:$IMAGE_TAG $initImage + # docker push $initImage + + # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + # embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" + # docker tag embedding:$IMAGE_TAG $embeddingImage + # docker push $embeddingImage - docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend - retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" - docker tag retrieval:$IMAGE_TAG $retrievalImage - docker push $retrievalImage - - docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend - vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" - docker tag vector:$IMAGE_TAG $vectorImage - docker push $vectorImage - - docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend - desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" - docker tag desktop:$IMAGE_TAG $desktopImage - docker push $desktopImage - - docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend - integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" - docker tag integration:$IMAGE_TAG $integrationImage - docker push $integrationImage - - docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend - crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" - docker tag crawling:$IMAGE_TAG $crawlingImage - docker push $crawlingImage - - # Replace placeholder image references with actual ECR URLs - sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml - sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml - sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml - sed -i "s|initImage|$initImage|g" backend/docker-compose-ec2.yaml - sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose-ec2.yaml - sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose-ec2.yaml - sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml - sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml - sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml + # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + # retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" + # docker tag retrieval:$IMAGE_TAG $retrievalImage + # docker push $retrievalImage + + # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + # vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" + # docker tag vector:$IMAGE_TAG $vectorImage + # docker push $vectorImage + + # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + # desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" + # docker tag desktop:$IMAGE_TAG $desktopImage + # docker push $desktopImage + + # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + # integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" + # docker tag integration:$IMAGE_TAG $integrationImage + # docker push $integrationImage + + # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + # crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" + # docker tag crawling:$IMAGE_TAG $crawlingImage + # docker push $crawlingImage + + # # Replace placeholder image references with actual ECR URLs + # sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|initImage|$initImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml + # sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml - name: Extract EC2 Key Pair run: | @@ -432,7 +432,7 @@ jobs: - name: Predeploy Cleanup run: | # SSH into EC2 to create directories first for clean up - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Print current directory and list contents pwd From 7d516f1ca95295e86d6322482342405105d8471d Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:11:38 -0400 Subject: [PATCH 108/160] fix: testing --- .github/workflows/EC2_deploy.yaml | 46 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index fd733710..d8b1f6ca 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -18,34 +18,34 @@ jobs: contents: read steps: - - name: Checkout Repository - uses: actions/checkout@v2 + # - name: Checkout Repository + # uses: actions/checkout@v2 - - name: Install Protocol Buffers Compiler - run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler + # - name: Install Protocol Buffers Compiler + # run: | + # sudo apt-get update + # sudo apt-get install -y protobuf-compiler - - name: Install Go - run: | - sudo apt-get update - sudo apt-get install -y golang-go + # - name: Install Go + # run: | + # sudo apt-get update + # sudo apt-get install -y golang-go - - name: Install protoc-gen-go - run: | - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + # - name: Install protoc-gen-go + # run: | + # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - name: Install protoc-gen-go-grpc - run: | - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + # - name: Install protoc-gen-go-grpc + # run: | + # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - name: Check protoc-gen-go Installation - run: | - protoc-gen-go --version || echo "protoc-gen-go not found" + # - name: Check protoc-gen-go Installation + # run: | + # protoc-gen-go --version || echo "protoc-gen-go not found" # - name: Generate .env Files # env: From e77ec677c8a55310a325a2feb01746737edfe056 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:16:34 -0400 Subject: [PATCH 109/160] fix: set fixed images --- .github/workflows/EC2_deploy.yaml | 124 +++++++++++++++--------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index d8b1f6ca..cbbfc8a6 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -8,7 +8,7 @@ on: env: AWS_REGION: us-east-1 ECR_REPOSITORY_NAMESPACE: indeq - IMAGE_TAG: ${{ github.run_id }} + IMAGE_TAG: "14586775921" jobs: deploy: @@ -353,68 +353,68 @@ jobs: # run: | # aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - # - name: Build and Push Docker Images - # run: | - # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend - # authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" - # docker tag authentication:$IMAGE_TAG $authImage - # docker push $authImage - - # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend - # queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" - # docker tag query:$IMAGE_TAG $queryImage - # docker push $queryImage - - # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend - # gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" - # docker tag gateway:$IMAGE_TAG $gatewayImage - # docker push $gatewayImage - - # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend - # initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" - # docker tag init:$IMAGE_TAG $initImage - # docker push $initImage - - # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend - # embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" - # docker tag embedding:$IMAGE_TAG $embeddingImage - # docker push $embeddingImage + - name: Build and Push Docker Images + run: | + # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" + # docker tag authentication:$IMAGE_TAG $authImage + # docker push $authImage + + # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" + # docker tag query:$IMAGE_TAG $queryImage + # docker push $queryImage + + # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" + # docker tag gateway:$IMAGE_TAG $gatewayImage + # docker push $gatewayImage + + # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" + # docker tag init:$IMAGE_TAG $initImage + # docker push $initImage + + # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" + # docker tag embedding:$IMAGE_TAG $embeddingImage + # docker push $embeddingImage - # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend - # retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" - # docker tag retrieval:$IMAGE_TAG $retrievalImage - # docker push $retrievalImage - - # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend - # vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" - # docker tag vector:$IMAGE_TAG $vectorImage - # docker push $vectorImage - - # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend - # desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" - # docker tag desktop:$IMAGE_TAG $desktopImage - # docker push $desktopImage - - # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend - # integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" - # docker tag integration:$IMAGE_TAG $integrationImage - # docker push $integrationImage - - # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend - # crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" - # docker tag crawling:$IMAGE_TAG $crawlingImage - # docker push $crawlingImage - - # # Replace placeholder image references with actual ECR URLs - # sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|initImage|$initImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml - # sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml + # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" + # docker tag retrieval:$IMAGE_TAG $retrievalImage + # docker push $retrievalImage + + # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" + # docker tag vector:$IMAGE_TAG $vectorImage + # docker push $vectorImage + + # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" + # docker tag desktop:$IMAGE_TAG $desktopImage + # docker push $desktopImage + + # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" + # docker tag integration:$IMAGE_TAG $integrationImage + # docker push $integrationImage + + # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" + # docker tag crawling:$IMAGE_TAG $crawlingImage + # docker push $crawlingImage + + # Replace placeholder image references with actual ECR URLs + sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml + sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml + sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml + sed -i "s|initImage|$initImage|g" backend/docker-compose-ec2.yaml + sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose-ec2.yaml + sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose-ec2.yaml + sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml + sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml + sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml - name: Extract EC2 Key Pair run: | From 8c9ec34b681a82f1037dcef83899269d183914d6 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:17:23 -0400 Subject: [PATCH 110/160] fix: added back checkout --- .github/workflows/EC2_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index cbbfc8a6..8b54430d 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -18,8 +18,8 @@ jobs: contents: read steps: - # - name: Checkout Repository - # uses: actions/checkout@v2 + - name: Checkout Repository + uses: actions/checkout@v2 # - name: Install Protocol Buffers Compiler # run: | From 1c6f6c72a1dfd11f12b4276df26dfb8ab0a0b9df Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:21:58 -0400 Subject: [PATCH 111/160] fix: added verbosity flag --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 8b54430d..9e1ef605 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -475,7 +475,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From c59821dca9eea967107d58aadbaaa82656cf3824 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:40:39 -0400 Subject: [PATCH 112/160] fix: debugging --- .github/workflows/EC2_deploy.yaml | 36 +++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 9e1ef605..0b78c354 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -462,8 +462,8 @@ jobs: - name: Copy docker-compose file to EC2 run: | # Copy the docker-compose file to EC2 - scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ + scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - name: Deploy to EC2 run: | @@ -487,25 +487,25 @@ jobs: echo "Docker installed successfully" fi - # Install Docker Compose V2 if not present - if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then - echo "Installing Docker Compose V2..." - # For Amazon Linux 2023 - sudo mkdir -p /usr/local/lib/docker/cli-plugins - sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose - sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose - fi + # # Install Docker Compose V2 if not present + # if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then + # echo "Installing Docker Compose V2..." + # # For Amazon Linux 2023 + # sudo mkdir -p /usr/local/lib/docker/cli-plugins + # sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose + # sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose + # fi - # Configure AWS CLI - aws configure set region us-east-1 + # # Configure AWS CLI + # aws configure set region us-east-1 - # Verify IAM role - aws sts get-caller-identity + # # Verify IAM role + # aws sts get-caller-identity - # Login to ECR without credentials (using IAM role) - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + # # Login to ECR without credentials (using IAM role) + # aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - # Run docker compose with absolute path - docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d + # # Run docker compose with absolute path + # docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d EOF From ecf51bb79451d17a715acfc2d31551d6e7499d94 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:42:24 -0400 Subject: [PATCH 113/160] fix: debugging, adding docker compose v2 --- .github/workflows/EC2_deploy.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 0b78c354..1fcd26b4 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -487,14 +487,14 @@ jobs: echo "Docker installed successfully" fi - # # Install Docker Compose V2 if not present - # if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then - # echo "Installing Docker Compose V2..." - # # For Amazon Linux 2023 - # sudo mkdir -p /usr/local/lib/docker/cli-plugins - # sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose - # sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose - # fi + # Install Docker Compose V2 if not present + if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then + echo "Installing Docker Compose V2..." + # For Amazon Linux 2023 + sudo mkdir -p /usr/local/lib/docker/cli-plugins + sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose + sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose + fi # # Configure AWS CLI # aws configure set region us-east-1 From 7fc08faaefb4f85180c72591b2b179eb0ddd1b91 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:43:21 -0400 Subject: [PATCH 114/160] fix: debugging, trying to set aws creds --- .github/workflows/EC2_deploy.yaml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 1fcd26b4..7bf5068d 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -496,14 +496,14 @@ jobs: sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose fi - # # Configure AWS CLI - # aws configure set region us-east-1 + # Configure AWS CLI + aws configure set region us-east-1 - # # Verify IAM role - # aws sts get-caller-identity + # Verify IAM role + aws sts get-caller-identity - # # Login to ECR without credentials (using IAM role) - # aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com + # Login to ECR without credentials (using IAM role) + aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com # # Run docker compose with absolute path # docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d From e10f91b2bf91ec158b77fb8b9a06985cb683159a Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:44:04 -0400 Subject: [PATCH 115/160] fix: debugging, trying docker compose command --- .github/workflows/EC2_deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 7bf5068d..92fb50d5 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -505,7 +505,7 @@ jobs: # Login to ECR without credentials (using IAM role) aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.us-east-1.amazonaws.com - # # Run docker compose with absolute path - # docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d + # Run docker compose with absolute path + docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d EOF From f1bdb6e976209a1a392b4969708bb97c674912ea Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:49:03 -0400 Subject: [PATCH 116/160] fix: added .env compilation process --- .github/workflows/EC2_deploy.yaml | 572 +++++++++++++++--------------- 1 file changed, 286 insertions(+), 286 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 92fb50d5..e92bf491 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -47,293 +47,293 @@ jobs: # run: | # protoc-gen-go --version || echo "protoc-gen-go not found" - # - name: Generate .env Files - # env: - # AUTH_PORT: ${{ secrets.AUTH_PORT }} - # DEV_PROD: ${{ secrets.DEV_PROD }} - # AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} - # QUERY_PORT: ${{ secrets.QUERY_PORT }} - # QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} - # VECTOR_PORT: ${{ secrets.VECTOR_PORT }} - # VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} - # DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} - # DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} - # RETRIEVAL_ADDRESS: ${{ secrets.RETRIEVAL_ADDRESS }} - # RETRIEVAL_PORT: ${{ secrets.RETRIEVAL_PORT }} - # WAITLIST_ADDRESS: ${{ secrets.WAITLIST_ADDRESS }} - # WAITLIST_PORT: ${{ secrets.WAITLIST_PORT }} - # INTEGRATION_ADDRESS: ${{ secrets.INTEGRATION_ADDRESS }} - # INTEGRATION_PORT: ${{ secrets.INTEGRATION_PORT }} - # GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} - # CRAWLING_PORT: ${{ secrets.CRAWLING_PORT }} - # CRAWLING_ADDRESS: ${{ secrets.CRAWLING_ADDRESS }} - # COUCHDB_ADDRESS: ${{ secrets.COUCHDB_ADDRESS }} - # MQTT_ADDRESS: ${{ secrets.MQTT_ADDRESS }} - # MQTT_PORT: ${{ secrets.MQTT_PORT }} - # EMBEDDING_MODEL_NAME: ${{ secrets.EMBEDDING_MODEL_NAME }} - # EMBEDDING_RERANKER: ${{ secrets.EMBEDDING_RERANKER }} - # EMBEDDING_PORT: ${{ secrets.EMBEDDING_PORT }} - # EMBEDDING_ADDRESS: ${{ secrets.EMBEDDING_ADDRESS }} - # EMBEDDING_MAX_WORKERS: ${{ secrets.EMBEDDING_MAX_WORKERS }} - # EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT: ${{ secrets.EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT }} - # CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS }} - # CRAWLING_GMAIL_MAX_WORKERS: ${{ secrets.CRAWLING_GMAIL_MAX_WORKERS }} - # CRAWLING_DRIVE_MAX_WORKERS: ${{ secrets.CRAWLING_DRIVE_MAX_WORKERS }} - # CRAWLING_GOOGLEDOCS_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEDOCS_MAX_WORKERS }} - # CRAWLING_GOOGLESLIDES_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLESLIDES_MAX_WORKERS }} - # CRAWLING_GOOGLEPDF_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEPDF_MAX_WORKERS }} - # CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS }} - # VECTOR_BATCH_SIZE: ${{ secrets.VECTOR_BATCH_SIZE }} - # VECTOR_BATCH_TIMEOUT: ${{ secrets.VECTOR_BATCH_TIMEOUT }} - # QUERY_NUMBER_OF_SOURCES: ${{ secrets.QUERY_NUMBER_OF_SOURCES }} - # QUERY_TTL: ${{ secrets.QUERY_TTL }} - # QUERY_QUEUE_TTL: ${{ secrets.QUERY_QUEUE_TTL }} - # QUERY_EXPANSION: ${{ secrets.QUERY_EXPANSION }} - # QUERY_SUMMARY_UPPER_BOUND: ${{ secrets.QUERY_SUMMARY_UPPER_BOUND }} - # QUERY_SUMMARY_LOWER_BOUND: ${{ secrets.QUERY_SUMMARY_LOWER_BOUND }} - # QUERY_DEFAULT_MODEL: ${{ secrets.QUERY_DEFAULT_MODEL }} - # RETRIEVAL_K_VAL: ${{ secrets.RETRIEVAL_K_VAL }} - # DATABASE_URL: ${{ secrets.DATABASE_URL }} - # POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - # POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - # POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - # COUCHDB_USER: ${{ secrets.COUCHDB_USER }} - # COUCHDB_PASSWORD: ${{ secrets.COUCHDB_PASSWORD }} - # JWT_SECRET: ${{ secrets.JWT_SECRET }} - # ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} - # ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} - # ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} - # ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} - # ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} - # MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} - # MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} - # MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} - # RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} - # RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} - # RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} - # RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} - # KAFKA_BROKER_ADDRESS: ${{ secrets.KAFKA_BROKER_ADDRESS }} - # KAFKA_BROKER_ID: ${{ secrets.KAFKA_BROKER_ID }} - # KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${{ secrets.KAFKA_LISTENER_SECURITY_PROTOCOL_MAP }} - # KAFKA_ADVERTISED_LISTENERS: ${{ secrets.KAFKA_ADVERTISED_LISTENERS }} - # KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${{ secrets.KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR }} - # KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: ${{ secrets.KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS }} - # KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_MIN_ISR }} - # KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR }} - # KAFKA_PROCESS_ROLES: ${{ secrets.KAFKA_PROCESS_ROLES }} - # KAFKA_NODE_ID: ${{ secrets.KAFKA_NODE_ID }} - # KAFKA_CONTROLLER_QUORUM_VOTERS: ${{ secrets.KAFKA_CONTROLLER_QUORUM_VOTERS }} - # KAFKA_LISTENERS: ${{ secrets.KAFKA_LISTENERS }} - # KAFKA_INTER_BROKER_LISTENER_NAME: ${{ secrets.KAFKA_INTER_BROKER_LISTENER_NAME }} - # KAFKA_CONTROLLER_LISTENER_NAMES: ${{ secrets.KAFKA_CONTROLLER_LISTENER_NAMES }} - # CLUSTER_ID: ${{ secrets.CLUSTER_ID }} - # KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} - # KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} - # OLLAMA_URL: ${{ secrets.OLLAMA_URL }} - # GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - # DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }} - # OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - # LLM_MODEL: ${{ secrets.LLM_MODEL }} - # ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} - # ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} - # VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} - # GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} - # GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} - # GOOGLE_AUTH_URL: ${{ secrets.GOOGLE_AUTH_URL }} - # GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} - # GOOGLE_SCOPES: ${{ secrets.GOOGLE_SCOPES }} - # GOOGLE_SSO_CLIENT_ID: ${{ secrets.GOOGLE_SSO_CLIENT_ID }} - # GOOGLE_SSO_CLIENT_SECRET: ${{ secrets.GOOGLE_SSO_CLIENT_SECRET }} - # GOOGLE_SSO_REDIRECT_URI: ${{ secrets.GOOGLE_SSO_REDIRECT_URI }} - # GOOGLE_SSO_SCOPES: ${{ secrets.GOOGLE_SSO_SCOPES }} - # MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} - # MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} - # MICROSOFT_AUTH_URL: ${{ secrets.MICROSOFT_AUTH_URL }} - # MICROSOFT_REDIRECT_URI: ${{ secrets.MICROSOFT_REDIRECT_URI }} - # MICROSOFT_SCOPES: ${{ secrets.MICROSOFT_SCOPES }} - # NOTION_CLIENT_ID: ${{ secrets.NOTION_CLIENT_ID }} - # NOTION_CLIENT_SECRET: ${{ secrets.NOTION_CLIENT_SECRET }} - # NOTION_REDIRECT_URI: ${{ secrets.NOTION_REDIRECT_URI }} - # NOTION_AUTH_URL: ${{ secrets.NOTION_AUTH_URL }} - # REDIS_ADDRESS: ${{ secrets.REDIS_ADDRESS }} - # REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} - # ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} - # CA_CRT: ${{ secrets.CA_CRT }} - # CA_KEY: ${{ secrets.CA_KEY }} - # QUERY_CRT: ${{ secrets.QUERY_CRT }} - # QUERY_KEY: ${{ secrets.QUERY_KEY }} - # AUTH_CRT: ${{ secrets.AUTH_CRT }} - # AUTH_KEY: ${{ secrets.AUTH_KEY }} - # VECTOR_CRT: ${{ secrets.VECTOR_CRT }} - # VECTOR_KEY: ${{ secrets.VECTOR_KEY }} - # GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} - # GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} - # DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} - # DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} - # EMBEDDING_CRT: ${{ secrets.EMBEDDING_CRT }} - # EMBEDDING_KEY: ${{ secrets.EMBEDDING_KEY }} - # RETRIEVAL_CRT: ${{ secrets.RETRIEVAL_CRT }} - # RETRIEVAL_KEY: ${{ secrets.RETRIEVAL_KEY }} - # MQTT_CRT: ${{ secrets.MQTT_CRT }} - # MQTT_KEY: ${{ secrets.MQTT_KEY }} - # WAITLIST_CRT: ${{ secrets.WAITLIST_CRT }} - # WAITLIST_KEY: ${{ secrets.WAITLIST_KEY }} - # INTEGRATION_CRT: ${{ secrets.INTEGRATION_CRT }} - # INTEGRATION_KEY: ${{ secrets.INTEGRATION_KEY }} - # CRAWLING_CRT: ${{ secrets.CRAWLING_CRT }} - # CRAWLING_KEY: ${{ secrets.CRAWLING_KEY }} - # ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} - # SMTP_FROM: ${{ secrets.SMTP_FROM }} - # SMTP_USER: ${{ secrets.SMTP_USER }} - # SMTP_PASS: ${{ secrets.SMTP_PASS }} - # SMTP_HOST: ${{ secrets.SMTP_HOST }} - # SMTP_PORT: ${{ secrets.SMTP_PORT }} - # EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} - # EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} - # run: | - # echo "AUTH_PORT=$AUTH_PORT" > .env - # echo "DEV_PROD=$DEV_PROD" >> .env - # echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> .env - # echo "QUERY_PORT=$QUERY_PORT" >> .env - # echo "QUERY_ADDRESS=$QUERY_ADDRESS" >> .env - # echo "VECTOR_PORT=$VECTOR_PORT" >> .env - # echo "VECTOR_ADDRESS=$VECTOR_ADDRESS" >> .env - # echo "DESKTOP_ADDRESS=$DESKTOP_ADDRESS" >> .env - # echo "DESKTOP_PORT=$DESKTOP_PORT" >> .env - # echo "RETRIEVAL_ADDRESS=$RETRIEVAL_ADDRESS" >> .env - # echo "RETRIEVAL_PORT=$RETRIEVAL_PORT" >> .env - # echo "WAITLIST_ADDRESS=$WAITLIST_ADDRESS" >> .env - # echo "WAITLIST_PORT=$WAITLIST_PORT" >> .env - # echo "INTEGRATION_ADDRESS=$INTEGRATION_ADDRESS" >> .env - # echo "INTEGRATION_PORT=$INTEGRATION_PORT" >> .env - # echo "GATEWAY_ADDRESS=$GATEWAY_ADDRESS" >> .env - # echo "CRAWLING_PORT=$CRAWLING_PORT" >> .env - # echo "CRAWLING_ADDRESS=$CRAWLING_ADDRESS" >> .env - # echo "COUCHDB_ADDRESS=$COUCHDB_ADDRESS" >> .env - # echo "MQTT_ADDRESS=$MQTT_ADDRESS" >> .env - # echo "MQTT_PORT=$MQTT_PORT" >> .env - # echo "EMBEDDING_MODEL_NAME=$EMBEDDING_MODEL_NAME" >> .env - # echo "EMBEDDING_RERANKER=$EMBEDDING_RERANKER" >> .env - # echo "EMBEDDING_PORT=$EMBEDDING_PORT" >> .env - # echo "EMBEDDING_ADDRESS=$EMBEDDING_ADDRESS" >> .env - # echo "EMBEDDING_MAX_WORKERS=$EMBEDDING_MAX_WORKERS" >> .env - # echo "EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT=$EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT" >> .env - # echo "CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS=$CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS" >> .env - # echo "CRAWLING_GMAIL_MAX_WORKERS=$CRAWLING_GMAIL_MAX_WORKERS" >> .env - # echo "CRAWLING_DRIVE_MAX_WORKERS=$CRAWLING_DRIVE_MAX_WORKERS" >> .env - # echo "CRAWLING_GOOGLEDOCS_MAX_WORKERS=$CRAWLING_GOOGLEDOCS_MAX_WORKERS" >> .env - # echo "CRAWLING_GOOGLESLIDES_MAX_WORKERS=$CRAWLING_GOOGLESLIDES_MAX_WORKERS" >> .env - # echo "CRAWLING_GOOGLEPDF_MAX_WORKERS=$CRAWLING_GOOGLEPDF_MAX_WORKERS" >> .env - # echo "CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS=$CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS" >> .env - # echo "VECTOR_BATCH_SIZE=$VECTOR_BATCH_SIZE" >> .env - # echo "VECTOR_BATCH_TIMEOUT=$VECTOR_BATCH_TIMEOUT" >> .env - # echo "QUERY_NUMBER_OF_SOURCES=$QUERY_NUMBER_OF_SOURCES" >> .env - # echo "QUERY_TTL=$QUERY_TTL" >> .env - # echo "QUERY_QUEUE_TTL=$QUERY_QUEUE_TTL" >> .env - # echo "QUERY_EXPANSION=$QUERY_EXPANSION" >> .env - # echo "QUERY_SUMMARY_UPPER_BOUND=$QUERY_SUMMARY_UPPER_BOUND" >> .env - # echo "QUERY_SUMMARY_LOWER_BOUND=$QUERY_SUMMARY_LOWER_BOUND" >> .env - # echo "QUERY_DEFAULT_MODEL=$QUERY_DEFAULT_MODEL" >> .env - # echo "RETRIEVAL_K_VAL=$RETRIEVAL_K_VAL" >> .env - # echo "DATABASE_URL=$DATABASE_URL" >> .env - # echo "POSTGRES_USER=$POSTGRES_USER" >> .env - # echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env - # echo "POSTGRES_DB=$POSTGRES_DB" >> .env - # echo "COUCHDB_USER=$COUCHDB_USER" >> .env - # echo "COUCHDB_PASSWORD=$COUCHDB_PASSWORD" >> .env - # echo "JWT_SECRET=$JWT_SECRET" >> .env - # echo "ARGON2_MEMORY=$ARGON2_MEMORY" >> .env - # echo "ARGON2_ITERATIONS=$ARGON2_ITERATIONS" >> .env - # echo "ARGON2_PARALLELISM=$ARGON2_PARALLELISM" >> .env - # echo "ARGON2_SALT_LENGTH=$ARGON2_SALT_LENGTH" >> .env - # echo "ARGON2_KEY_LENGTH=$ARGON2_KEY_LENGTH" >> .env - # echo "MIN_PASSWORD_LENGTH=$MIN_PASSWORD_LENGTH" >> .env - # echo "MAX_PASSWORD_LENGTH=$MAX_PASSWORD_LENGTH" >> .env - # echo "MAX_EMAIL_LENGTH=$MAX_EMAIL_LENGTH" >> .env - # echo "RABBITMQ_URL=$RABBITMQ_URL" >> .env - # echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> .env - # echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> .env - # echo "RABBITMQ_LOGS=$RABBITMQ_LOGS" >> .env - # echo "KAFKA_BROKER_ADDRESS=$KAFKA_BROKER_ADDRESS" >> .env - # echo "KAFKA_BROKER_ID=$KAFKA_BROKER_ID" >> .env - # echo "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=$KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" >> .env - # echo "KAFKA_ADVERTISED_LISTENERS=$KAFKA_ADVERTISED_LISTENERS" >> .env - # echo "KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=$KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" >> .env - # echo "KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=$KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" >> .env - # echo "KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=$KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" >> .env - # echo "KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=$KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" >> .env - # echo "KAFKA_PROCESS_ROLES=$KAFKA_PROCESS_ROLES" >> .env - # echo "KAFKA_NODE_ID=$KAFKA_NODE_ID" >> .env - # echo "KAFKA_CONTROLLER_QUORUM_VOTERS=$KAFKA_CONTROLLER_QUORUM_VOTERS" >> .env - # echo "KAFKA_LISTENERS=$KAFKA_LISTENERS" >> .env - # echo "KAFKA_INTER_BROKER_LISTENER_NAME=$KAFKA_INTER_BROKER_LISTENER_NAME" >> .env - # echo "KAFKA_CONTROLLER_LISTENER_NAMES=$KAFKA_CONTROLLER_LISTENER_NAMES" >> .env - # echo "CLUSTER_ID=$CLUSTER_ID" >> .env - # echo "KAFKA_LOG4J_ROOT_LOGLEVEL=$KAFKA_LOG4J_ROOT_LOGLEVEL" >> .env - # echo "KAFKA_LOG4J_LOGGERS=$KAFKA_LOG4J_LOGGERS" >> .env - # echo "OLLAMA_URL=$OLLAMA_URL" >> .env - # echo "GEMINI_API_KEY=$GEMINI_API_KEY" >> .env - # echo "DEEPINFRA_API_KEY=$DEEPINFRA_API_KEY" >> .env - # echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env - # echo "LLM_MODEL=$LLM_MODEL" >> .env - # echo "ZILLIZ_ADDRESS=$ZILLIZ_ADDRESS" >> .env - # echo "ZILLIZ_API_KEY=$ZILLIZ_API_KEY" >> .env - # echo "VECTOR_DIMENSION=$VECTOR_DIMENSION" >> .env - # echo "GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID" >> .env - # echo "GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET" >> .env - # echo "GOOGLE_AUTH_URL=$GOOGLE_AUTH_URL" >> .env - # echo "GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI" >> .env - # echo "GOOGLE_SCOPES=$GOOGLE_SCOPES" >> .env - # echo "GOOGLE_SSO_CLIENT_ID=$GOOGLE_SSO_CLIENT_ID" >> .env - # echo "GOOGLE_SSO_CLIENT_SECRET=$GOOGLE_SSO_CLIENT_SECRET" >> .env - # echo "GOOGLE_SSO_REDIRECT_URI=$GOOGLE_SSO_REDIRECT_URI" >> .env - # echo "GOOGLE_SSO_SCOPES=$GOOGLE_SSO_SCOPES" >> .env - # echo "MICROSOFT_CLIENT_ID=$MICROSOFT_CLIENT_ID" >> .env - # echo "MICROSOFT_CLIENT_SECRET=$MICROSOFT_CLIENT_SECRET" >> .env - # echo "MICROSOFT_AUTH_URL=$MICROSOFT_AUTH_URL" >> .env - # echo "MICROSOFT_REDIRECT_URI=$MICROSOFT_REDIRECT_URI" >> .env - # echo "MICROSOFT_SCOPES=$MICROSOFT_SCOPES" >> .env - # echo "NOTION_CLIENT_ID=$NOTION_CLIENT_ID" >> .env - # echo "NOTION_CLIENT_SECRET=$NOTION_CLIENT_SECRET" >> .env - # echo "NOTION_REDIRECT_URI=$NOTION_REDIRECT_URI" >> .env - # echo "NOTION_AUTH_URL=$NOTION_AUTH_URL" >> .env - # echo "REDIS_ADDRESS=$REDIS_ADDRESS" >> .env - # echo "REDIS_PASSWORD=$REDIS_PASSWORD" >> .env - # echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> .env - # echo "CA_CRT=$CA_CRT" >> .env - # echo "CA_KEY=$CA_KEY" >> .env - # echo "QUERY_CRT=$QUERY_CRT" >> .env - # echo "QUERY_KEY=$QUERY_KEY" >> .env - # echo "AUTH_CRT=$AUTH_CRT" >> .env - # echo "AUTH_KEY=$AUTH_KEY" >> .env - # echo "VECTOR_CRT=$VECTOR_CRT" >> .env - # echo "VECTOR_KEY=$VECTOR_KEY" >> .env - # echo "GATEWAY_CRT=$GATEWAY_CRT" >> .env - # echo "GATEWAY_KEY=$GATEWAY_KEY" >> .env - # echo "DESKTOP_CRT=$DESKTOP_CRT" >> .env - # echo "DESKTOP_KEY=$DESKTOP_KEY" >> .env - # echo "EMBEDDING_CRT=$EMBEDDING_CRT" >> .env - # echo "EMBEDDING_KEY=$EMBEDDING_KEY" >> .env - # echo "RETRIEVAL_CRT=$RETRIEVAL_CRT" >> .env - # echo "RETRIEVAL_KEY=$RETRIEVAL_KEY" >> .env - # echo "MQTT_CRT=$MQTT_CRT" >> .env - # echo "MQTT_KEY=$MQTT_KEY" >> .env - # echo "WAITLIST_CRT=$WAITLIST_CRT" >> .env - # echo "WAITLIST_KEY=$WAITLIST_KEY" >> .env - # echo "INTEGRATION_CRT=$INTEGRATION_CRT" >> .env - # echo "INTEGRATION_KEY=$INTEGRATION_KEY" >> .env - # echo "CRAWLING_CRT=$CRAWLING_CRT" >> .env - # echo "CRAWLING_KEY=$CRAWLING_KEY" >> .env - # echo "ALLOWED_CLIENT_IP=$ALLOWED_CLIENT_IP" >> .env - # echo "SMTP_FROM=$SMTP_FROM" >> .env - # echo "SMTP_USER=$SMTP_USER" >> .env - # echo "SMTP_PASS=$SMTP_PASS" >> .env - # echo "SMTP_HOST=$SMTP_HOST" >> .env - # echo "SMTP_PORT=$SMTP_PORT" >> .env - # echo "EC2_KEY_PAIR=$EC2_KEY_PAIR" >> .env - # echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env + - name: Generate .env Files + env: + AUTH_PORT: ${{ secrets.AUTH_PORT }} + DEV_PROD: ${{ secrets.DEV_PROD }} + AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} + QUERY_PORT: ${{ secrets.QUERY_PORT }} + QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} + VECTOR_PORT: ${{ secrets.VECTOR_PORT }} + VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} + DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} + DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} + RETRIEVAL_ADDRESS: ${{ secrets.RETRIEVAL_ADDRESS }} + RETRIEVAL_PORT: ${{ secrets.RETRIEVAL_PORT }} + WAITLIST_ADDRESS: ${{ secrets.WAITLIST_ADDRESS }} + WAITLIST_PORT: ${{ secrets.WAITLIST_PORT }} + INTEGRATION_ADDRESS: ${{ secrets.INTEGRATION_ADDRESS }} + INTEGRATION_PORT: ${{ secrets.INTEGRATION_PORT }} + GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} + CRAWLING_PORT: ${{ secrets.CRAWLING_PORT }} + CRAWLING_ADDRESS: ${{ secrets.CRAWLING_ADDRESS }} + COUCHDB_ADDRESS: ${{ secrets.COUCHDB_ADDRESS }} + MQTT_ADDRESS: ${{ secrets.MQTT_ADDRESS }} + MQTT_PORT: ${{ secrets.MQTT_PORT }} + EMBEDDING_MODEL_NAME: ${{ secrets.EMBEDDING_MODEL_NAME }} + EMBEDDING_RERANKER: ${{ secrets.EMBEDDING_RERANKER }} + EMBEDDING_PORT: ${{ secrets.EMBEDDING_PORT }} + EMBEDDING_ADDRESS: ${{ secrets.EMBEDDING_ADDRESS }} + EMBEDDING_MAX_WORKERS: ${{ secrets.EMBEDDING_MAX_WORKERS }} + EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT: ${{ secrets.EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT }} + CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS }} + CRAWLING_GMAIL_MAX_WORKERS: ${{ secrets.CRAWLING_GMAIL_MAX_WORKERS }} + CRAWLING_DRIVE_MAX_WORKERS: ${{ secrets.CRAWLING_DRIVE_MAX_WORKERS }} + CRAWLING_GOOGLEDOCS_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEDOCS_MAX_WORKERS }} + CRAWLING_GOOGLESLIDES_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLESLIDES_MAX_WORKERS }} + CRAWLING_GOOGLEPDF_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEPDF_MAX_WORKERS }} + CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS }} + VECTOR_BATCH_SIZE: ${{ secrets.VECTOR_BATCH_SIZE }} + VECTOR_BATCH_TIMEOUT: ${{ secrets.VECTOR_BATCH_TIMEOUT }} + QUERY_NUMBER_OF_SOURCES: ${{ secrets.QUERY_NUMBER_OF_SOURCES }} + QUERY_TTL: ${{ secrets.QUERY_TTL }} + QUERY_QUEUE_TTL: ${{ secrets.QUERY_QUEUE_TTL }} + QUERY_EXPANSION: ${{ secrets.QUERY_EXPANSION }} + QUERY_SUMMARY_UPPER_BOUND: ${{ secrets.QUERY_SUMMARY_UPPER_BOUND }} + QUERY_SUMMARY_LOWER_BOUND: ${{ secrets.QUERY_SUMMARY_LOWER_BOUND }} + QUERY_DEFAULT_MODEL: ${{ secrets.QUERY_DEFAULT_MODEL }} + RETRIEVAL_K_VAL: ${{ secrets.RETRIEVAL_K_VAL }} + DATABASE_URL: ${{ secrets.DATABASE_URL }} + POSTGRES_USER: ${{ secrets.POSTGRES_USER }} + POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_DB: ${{ secrets.POSTGRES_DB }} + COUCHDB_USER: ${{ secrets.COUCHDB_USER }} + COUCHDB_PASSWORD: ${{ secrets.COUCHDB_PASSWORD }} + JWT_SECRET: ${{ secrets.JWT_SECRET }} + ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} + ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} + ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} + ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} + ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} + MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} + MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} + MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} + RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} + RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} + RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} + RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} + KAFKA_BROKER_ADDRESS: ${{ secrets.KAFKA_BROKER_ADDRESS }} + KAFKA_BROKER_ID: ${{ secrets.KAFKA_BROKER_ID }} + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${{ secrets.KAFKA_LISTENER_SECURITY_PROTOCOL_MAP }} + KAFKA_ADVERTISED_LISTENERS: ${{ secrets.KAFKA_ADVERTISED_LISTENERS }} + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${{ secrets.KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR }} + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: ${{ secrets.KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS }} + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_MIN_ISR }} + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR }} + KAFKA_PROCESS_ROLES: ${{ secrets.KAFKA_PROCESS_ROLES }} + KAFKA_NODE_ID: ${{ secrets.KAFKA_NODE_ID }} + KAFKA_CONTROLLER_QUORUM_VOTERS: ${{ secrets.KAFKA_CONTROLLER_QUORUM_VOTERS }} + KAFKA_LISTENERS: ${{ secrets.KAFKA_LISTENERS }} + KAFKA_INTER_BROKER_LISTENER_NAME: ${{ secrets.KAFKA_INTER_BROKER_LISTENER_NAME }} + KAFKA_CONTROLLER_LISTENER_NAMES: ${{ secrets.KAFKA_CONTROLLER_LISTENER_NAMES }} + CLUSTER_ID: ${{ secrets.CLUSTER_ID }} + KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} + KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} + OLLAMA_URL: ${{ secrets.OLLAMA_URL }} + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + LLM_MODEL: ${{ secrets.LLM_MODEL }} + ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} + ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} + VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} + GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} + GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} + GOOGLE_AUTH_URL: ${{ secrets.GOOGLE_AUTH_URL }} + GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} + GOOGLE_SCOPES: ${{ secrets.GOOGLE_SCOPES }} + GOOGLE_SSO_CLIENT_ID: ${{ secrets.GOOGLE_SSO_CLIENT_ID }} + GOOGLE_SSO_CLIENT_SECRET: ${{ secrets.GOOGLE_SSO_CLIENT_SECRET }} + GOOGLE_SSO_REDIRECT_URI: ${{ secrets.GOOGLE_SSO_REDIRECT_URI }} + GOOGLE_SSO_SCOPES: ${{ secrets.GOOGLE_SSO_SCOPES }} + MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} + MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} + MICROSOFT_AUTH_URL: ${{ secrets.MICROSOFT_AUTH_URL }} + MICROSOFT_REDIRECT_URI: ${{ secrets.MICROSOFT_REDIRECT_URI }} + MICROSOFT_SCOPES: ${{ secrets.MICROSOFT_SCOPES }} + NOTION_CLIENT_ID: ${{ secrets.NOTION_CLIENT_ID }} + NOTION_CLIENT_SECRET: ${{ secrets.NOTION_CLIENT_SECRET }} + NOTION_REDIRECT_URI: ${{ secrets.NOTION_REDIRECT_URI }} + NOTION_AUTH_URL: ${{ secrets.NOTION_AUTH_URL }} + REDIS_ADDRESS: ${{ secrets.REDIS_ADDRESS }} + REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} + ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} + CA_CRT: ${{ secrets.CA_CRT }} + CA_KEY: ${{ secrets.CA_KEY }} + QUERY_CRT: ${{ secrets.QUERY_CRT }} + QUERY_KEY: ${{ secrets.QUERY_KEY }} + AUTH_CRT: ${{ secrets.AUTH_CRT }} + AUTH_KEY: ${{ secrets.AUTH_KEY }} + VECTOR_CRT: ${{ secrets.VECTOR_CRT }} + VECTOR_KEY: ${{ secrets.VECTOR_KEY }} + GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} + GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} + DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} + DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} + EMBEDDING_CRT: ${{ secrets.EMBEDDING_CRT }} + EMBEDDING_KEY: ${{ secrets.EMBEDDING_KEY }} + RETRIEVAL_CRT: ${{ secrets.RETRIEVAL_CRT }} + RETRIEVAL_KEY: ${{ secrets.RETRIEVAL_KEY }} + MQTT_CRT: ${{ secrets.MQTT_CRT }} + MQTT_KEY: ${{ secrets.MQTT_KEY }} + WAITLIST_CRT: ${{ secrets.WAITLIST_CRT }} + WAITLIST_KEY: ${{ secrets.WAITLIST_KEY }} + INTEGRATION_CRT: ${{ secrets.INTEGRATION_CRT }} + INTEGRATION_KEY: ${{ secrets.INTEGRATION_KEY }} + CRAWLING_CRT: ${{ secrets.CRAWLING_CRT }} + CRAWLING_KEY: ${{ secrets.CRAWLING_KEY }} + ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} + SMTP_FROM: ${{ secrets.SMTP_FROM }} + SMTP_USER: ${{ secrets.SMTP_USER }} + SMTP_PASS: ${{ secrets.SMTP_PASS }} + SMTP_HOST: ${{ secrets.SMTP_HOST }} + SMTP_PORT: ${{ secrets.SMTP_PORT }} + EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} + EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} + run: | + echo "AUTH_PORT=$AUTH_PORT" > .env + echo "DEV_PROD=$DEV_PROD" >> .env + echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> .env + echo "QUERY_PORT=$QUERY_PORT" >> .env + echo "QUERY_ADDRESS=$QUERY_ADDRESS" >> .env + echo "VECTOR_PORT=$VECTOR_PORT" >> .env + echo "VECTOR_ADDRESS=$VECTOR_ADDRESS" >> .env + echo "DESKTOP_ADDRESS=$DESKTOP_ADDRESS" >> .env + echo "DESKTOP_PORT=$DESKTOP_PORT" >> .env + echo "RETRIEVAL_ADDRESS=$RETRIEVAL_ADDRESS" >> .env + echo "RETRIEVAL_PORT=$RETRIEVAL_PORT" >> .env + echo "WAITLIST_ADDRESS=$WAITLIST_ADDRESS" >> .env + echo "WAITLIST_PORT=$WAITLIST_PORT" >> .env + echo "INTEGRATION_ADDRESS=$INTEGRATION_ADDRESS" >> .env + echo "INTEGRATION_PORT=$INTEGRATION_PORT" >> .env + echo "GATEWAY_ADDRESS=$GATEWAY_ADDRESS" >> .env + echo "CRAWLING_PORT=$CRAWLING_PORT" >> .env + echo "CRAWLING_ADDRESS=$CRAWLING_ADDRESS" >> .env + echo "COUCHDB_ADDRESS=$COUCHDB_ADDRESS" >> .env + echo "MQTT_ADDRESS=$MQTT_ADDRESS" >> .env + echo "MQTT_PORT=$MQTT_PORT" >> .env + echo "EMBEDDING_MODEL_NAME=$EMBEDDING_MODEL_NAME" >> .env + echo "EMBEDDING_RERANKER=$EMBEDDING_RERANKER" >> .env + echo "EMBEDDING_PORT=$EMBEDDING_PORT" >> .env + echo "EMBEDDING_ADDRESS=$EMBEDDING_ADDRESS" >> .env + echo "EMBEDDING_MAX_WORKERS=$EMBEDDING_MAX_WORKERS" >> .env + echo "EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT=$EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT" >> .env + echo "CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS=$CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS" >> .env + echo "CRAWLING_GMAIL_MAX_WORKERS=$CRAWLING_GMAIL_MAX_WORKERS" >> .env + echo "CRAWLING_DRIVE_MAX_WORKERS=$CRAWLING_DRIVE_MAX_WORKERS" >> .env + echo "CRAWLING_GOOGLEDOCS_MAX_WORKERS=$CRAWLING_GOOGLEDOCS_MAX_WORKERS" >> .env + echo "CRAWLING_GOOGLESLIDES_MAX_WORKERS=$CRAWLING_GOOGLESLIDES_MAX_WORKERS" >> .env + echo "CRAWLING_GOOGLEPDF_MAX_WORKERS=$CRAWLING_GOOGLEPDF_MAX_WORKERS" >> .env + echo "CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS=$CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS" >> .env + echo "VECTOR_BATCH_SIZE=$VECTOR_BATCH_SIZE" >> .env + echo "VECTOR_BATCH_TIMEOUT=$VECTOR_BATCH_TIMEOUT" >> .env + echo "QUERY_NUMBER_OF_SOURCES=$QUERY_NUMBER_OF_SOURCES" >> .env + echo "QUERY_TTL=$QUERY_TTL" >> .env + echo "QUERY_QUEUE_TTL=$QUERY_QUEUE_TTL" >> .env + echo "QUERY_EXPANSION=$QUERY_EXPANSION" >> .env + echo "QUERY_SUMMARY_UPPER_BOUND=$QUERY_SUMMARY_UPPER_BOUND" >> .env + echo "QUERY_SUMMARY_LOWER_BOUND=$QUERY_SUMMARY_LOWER_BOUND" >> .env + echo "QUERY_DEFAULT_MODEL=$QUERY_DEFAULT_MODEL" >> .env + echo "RETRIEVAL_K_VAL=$RETRIEVAL_K_VAL" >> .env + echo "DATABASE_URL=$DATABASE_URL" >> .env + echo "POSTGRES_USER=$POSTGRES_USER" >> .env + echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env + echo "POSTGRES_DB=$POSTGRES_DB" >> .env + echo "COUCHDB_USER=$COUCHDB_USER" >> .env + echo "COUCHDB_PASSWORD=$COUCHDB_PASSWORD" >> .env + echo "JWT_SECRET=$JWT_SECRET" >> .env + echo "ARGON2_MEMORY=$ARGON2_MEMORY" >> .env + echo "ARGON2_ITERATIONS=$ARGON2_ITERATIONS" >> .env + echo "ARGON2_PARALLELISM=$ARGON2_PARALLELISM" >> .env + echo "ARGON2_SALT_LENGTH=$ARGON2_SALT_LENGTH" >> .env + echo "ARGON2_KEY_LENGTH=$ARGON2_KEY_LENGTH" >> .env + echo "MIN_PASSWORD_LENGTH=$MIN_PASSWORD_LENGTH" >> .env + echo "MAX_PASSWORD_LENGTH=$MAX_PASSWORD_LENGTH" >> .env + echo "MAX_EMAIL_LENGTH=$MAX_EMAIL_LENGTH" >> .env + echo "RABBITMQ_URL=$RABBITMQ_URL" >> .env + echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> .env + echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> .env + echo "RABBITMQ_LOGS=$RABBITMQ_LOGS" >> .env + echo "KAFKA_BROKER_ADDRESS=$KAFKA_BROKER_ADDRESS" >> .env + echo "KAFKA_BROKER_ID=$KAFKA_BROKER_ID" >> .env + echo "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=$KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" >> .env + echo "KAFKA_ADVERTISED_LISTENERS=$KAFKA_ADVERTISED_LISTENERS" >> .env + echo "KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=$KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" >> .env + echo "KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=$KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" >> .env + echo "KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=$KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" >> .env + echo "KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=$KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" >> .env + echo "KAFKA_PROCESS_ROLES=$KAFKA_PROCESS_ROLES" >> .env + echo "KAFKA_NODE_ID=$KAFKA_NODE_ID" >> .env + echo "KAFKA_CONTROLLER_QUORUM_VOTERS=$KAFKA_CONTROLLER_QUORUM_VOTERS" >> .env + echo "KAFKA_LISTENERS=$KAFKA_LISTENERS" >> .env + echo "KAFKA_INTER_BROKER_LISTENER_NAME=$KAFKA_INTER_BROKER_LISTENER_NAME" >> .env + echo "KAFKA_CONTROLLER_LISTENER_NAMES=$KAFKA_CONTROLLER_LISTENER_NAMES" >> .env + echo "CLUSTER_ID=$CLUSTER_ID" >> .env + echo "KAFKA_LOG4J_ROOT_LOGLEVEL=$KAFKA_LOG4J_ROOT_LOGLEVEL" >> .env + echo "KAFKA_LOG4J_LOGGERS=$KAFKA_LOG4J_LOGGERS" >> .env + echo "OLLAMA_URL=$OLLAMA_URL" >> .env + echo "GEMINI_API_KEY=$GEMINI_API_KEY" >> .env + echo "DEEPINFRA_API_KEY=$DEEPINFRA_API_KEY" >> .env + echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env + echo "LLM_MODEL=$LLM_MODEL" >> .env + echo "ZILLIZ_ADDRESS=$ZILLIZ_ADDRESS" >> .env + echo "ZILLIZ_API_KEY=$ZILLIZ_API_KEY" >> .env + echo "VECTOR_DIMENSION=$VECTOR_DIMENSION" >> .env + echo "GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID" >> .env + echo "GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET" >> .env + echo "GOOGLE_AUTH_URL=$GOOGLE_AUTH_URL" >> .env + echo "GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI" >> .env + echo "GOOGLE_SCOPES=$GOOGLE_SCOPES" >> .env + echo "GOOGLE_SSO_CLIENT_ID=$GOOGLE_SSO_CLIENT_ID" >> .env + echo "GOOGLE_SSO_CLIENT_SECRET=$GOOGLE_SSO_CLIENT_SECRET" >> .env + echo "GOOGLE_SSO_REDIRECT_URI=$GOOGLE_SSO_REDIRECT_URI" >> .env + echo "GOOGLE_SSO_SCOPES=$GOOGLE_SSO_SCOPES" >> .env + echo "MICROSOFT_CLIENT_ID=$MICROSOFT_CLIENT_ID" >> .env + echo "MICROSOFT_CLIENT_SECRET=$MICROSOFT_CLIENT_SECRET" >> .env + echo "MICROSOFT_AUTH_URL=$MICROSOFT_AUTH_URL" >> .env + echo "MICROSOFT_REDIRECT_URI=$MICROSOFT_REDIRECT_URI" >> .env + echo "MICROSOFT_SCOPES=$MICROSOFT_SCOPES" >> .env + echo "NOTION_CLIENT_ID=$NOTION_CLIENT_ID" >> .env + echo "NOTION_CLIENT_SECRET=$NOTION_CLIENT_SECRET" >> .env + echo "NOTION_REDIRECT_URI=$NOTION_REDIRECT_URI" >> .env + echo "NOTION_AUTH_URL=$NOTION_AUTH_URL" >> .env + echo "REDIS_ADDRESS=$REDIS_ADDRESS" >> .env + echo "REDIS_PASSWORD=$REDIS_PASSWORD" >> .env + echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> .env + echo "CA_CRT=$CA_CRT" >> .env + echo "CA_KEY=$CA_KEY" >> .env + echo "QUERY_CRT=$QUERY_CRT" >> .env + echo "QUERY_KEY=$QUERY_KEY" >> .env + echo "AUTH_CRT=$AUTH_CRT" >> .env + echo "AUTH_KEY=$AUTH_KEY" >> .env + echo "VECTOR_CRT=$VECTOR_CRT" >> .env + echo "VECTOR_KEY=$VECTOR_KEY" >> .env + echo "GATEWAY_CRT=$GATEWAY_CRT" >> .env + echo "GATEWAY_KEY=$GATEWAY_KEY" >> .env + echo "DESKTOP_CRT=$DESKTOP_CRT" >> .env + echo "DESKTOP_KEY=$DESKTOP_KEY" >> .env + echo "EMBEDDING_CRT=$EMBEDDING_CRT" >> .env + echo "EMBEDDING_KEY=$EMBEDDING_KEY" >> .env + echo "RETRIEVAL_CRT=$RETRIEVAL_CRT" >> .env + echo "RETRIEVAL_KEY=$RETRIEVAL_KEY" >> .env + echo "MQTT_CRT=$MQTT_CRT" >> .env + echo "MQTT_KEY=$MQTT_KEY" >> .env + echo "WAITLIST_CRT=$WAITLIST_CRT" >> .env + echo "WAITLIST_KEY=$WAITLIST_KEY" >> .env + echo "INTEGRATION_CRT=$INTEGRATION_CRT" >> .env + echo "INTEGRATION_KEY=$INTEGRATION_KEY" >> .env + echo "CRAWLING_CRT=$CRAWLING_CRT" >> .env + echo "CRAWLING_KEY=$CRAWLING_KEY" >> .env + echo "ALLOWED_CLIENT_IP=$ALLOWED_CLIENT_IP" >> .env + echo "SMTP_FROM=$SMTP_FROM" >> .env + echo "SMTP_USER=$SMTP_USER" >> .env + echo "SMTP_PASS=$SMTP_PASS" >> .env + echo "SMTP_HOST=$SMTP_HOST" >> .env + echo "SMTP_PORT=$SMTP_PORT" >> .env + echo "EC2_KEY_PAIR=$EC2_KEY_PAIR" >> .env + echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env - # mv .env backend/common/config/.env + mv .env backend/common/config/.env # - name: Generate Code # run: | From 712205f69240eaafe7a03e34bc6e5fd20f65ff93 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 01:58:47 -0400 Subject: [PATCH 117/160] fix: added updated docker-compose --- .github/workflows/EC2_deploy.yaml | 23 ++-- backend/docker-compose-ec2.yaml | 184 +++++++++++++++++++++++++----- 2 files changed, 171 insertions(+), 36 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index e92bf491..1568e9c6 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -406,15 +406,18 @@ jobs: # docker push $crawlingImage # Replace placeholder image references with actual ECR URLs - sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml - sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml - sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml - sed -i "s|initImage|$initImage|g" backend/docker-compose-ec2.yaml - sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose-ec2.yaml - sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose-ec2.yaml - sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml - sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml - sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml + sed -i "s|authImage|$authImage|g" backend/docker-compose.yaml + sed -i "s|queryImage|$queryImage|g" backend/docker-compose.yaml + sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose.yaml + sed -i "s|initImage|$initImage|g" backend/docker-compose.yaml + sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose.yaml + sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose.yaml + sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose.yaml + sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose.yaml + sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose.yaml + + # Checking if the docker-compose.yaml file is updated + cat backend/docker-compose.yaml - name: Extract EC2 Key Pair run: | @@ -462,7 +465,7 @@ jobs: - name: Copy docker-compose file to EC2 run: | # Copy the docker-compose file to EC2 - scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ + scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - name: Deploy to EC2 diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml index e4c55688..424fa3c9 100644 --- a/backend/docker-compose-ec2.yaml +++ b/backend/docker-compose-ec2.yaml @@ -1,35 +1,12 @@ services: - query: - image: queryImage - depends_on: - rabbitmq: - condition: service_healthy - networks: - - indeq-net - - # this docker setup doesn't utilize the GPU - # ollama: - # image: ollama/ollama:latest - # ports: - # - "11434:11434" - # volumes: - # - ollama_models:/root/.ollama - # environment: - # - OLLAMA_NUM_PARALLEL=4 # Number of threads to utilize - # mem_limit: 16g - # networks: - # - indeq-net - rabbitmq: - image: rabbitmq:3-management + image: rabbitmq:4.0-management container_name: rabbitmq env_file: - ./common/config/.env ports: - 5672:5672 # AMQP protocol port - 15672:15672 # Management UI port - volumes: - - rabbitmq_data:/var/lib/rabbitmq healthcheck: test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"] interval: 5s @@ -38,6 +15,52 @@ services: networks: - indeq-net + kafka: + image: apache/kafka:3.9.0 + container_name: kafka + env_file: + - ./common/config/.env + networks: + - indeq-net + healthcheck: + test: + [ + "CMD-SHELL", + "/opt/kafka/bin/kafka-topics.sh --bootstrap-server kafka:29092 --list", + ] + interval: 5s + timeout: 5s + retries: 5 + + init: + image: initImage + restart: "no" + depends_on: + kafka: + condition: service_healthy + couchdb: + condition: service_healthy + networks: + - indeq-net + + embedding: + image: embeddingImage + volumes: + - embedding_data:/app/model_cache + networks: + - indeq-net + + mqtt: + image: mqttImage + container_name: mqtt-service + ports: + - "8883:8883" + build: + context: . + dockerfile: mqtt/Dockerfile + networks: + - indeq-net + authentication: image: authImage env_file: @@ -45,11 +68,38 @@ services: depends_on: appDB: condition: service_healthy + vector: + condition: service_started + networks: + - indeq-net + + retrieval: + image: retrievalImage + depends_on: + desktop: + condition: service_started + crawling: + condition: service_started + vector: + condition: service_started + networks: + - indeq-net + + vector: + image: vectorImage + build: + context: . + dockerfile: vector/Dockerfile + depends_on: + init: + condition: service_completed_successfully networks: - indeq-net gateway: image: gatewayImage + env_file: + - ./common/config/.env ports: - "8080:8080" depends_on: @@ -57,6 +107,70 @@ services: condition: service_started rabbitmq: condition: service_healthy + authentication: + condition: service_started + desktop: + condition: service_started + init: + condition: service_completed_successfully + networks: + - indeq-net + + desktop: + image: desktopImage + ports: + - "8081:8080" + depends_on: + init: + condition: service_completed_successfully + mqtt: + condition: service_started + networks: + - indeq-net + + query: + image: queryImage + depends_on: + rabbitmq: + condition: service_healthy + networks: + - indeq-net + + crawling: + image: crawlingImage + env_file: + - ./common/config/.env + depends_on: + init: + condition: service_completed_successfully + appDB: + condition: service_healthy + networks: + - indeq-net + + integration: + image: integrationImage + env_file: ./common/config/.env + depends_on: + appDB: + condition: service_healthy + redis: + condition: service_started + networks: + - indeq-net + + redis: + image: redis:alpine + env_file: + - ./common/config/.env + command: ["sh", "-c", 'exec redis-server --requirepass "$$REDIS_PASSWORD"'] + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "$$REDIS_PASSWORD", "ping"] + interval: 5s + timeout: 3s + retries: 3 networks: - indeq-net @@ -78,12 +192,30 @@ services: - app_data:/var/lib/postgresql/data networks: - indeq-net + + couchdb: + image: couchdb + env_file: + - ./common/config/.env + restart: always + volumes: + - couch_data:/opt/couchdb/data + healthcheck: + test: ["CMD-SHELL", "curl --silent --fail http://localhost:5984 || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + networks: + - indeq-net volumes: app_data: - ollama_models: - rabbitmq_data: + embedding_data: + redis_data: + couch_data: networks: indeq-net: driver: bridge + labels: + com.docker.network.dns.name: docker.internal From 392ac70b2629235149b39e8b31f386a74b353bcc Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 02:07:05 -0400 Subject: [PATCH 118/160] fix: debugging w/o vvv flag --- .github/workflows/EC2_deploy.yaml | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 1568e9c6..862e3a12 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -406,18 +406,18 @@ jobs: # docker push $crawlingImage # Replace placeholder image references with actual ECR URLs - sed -i "s|authImage|$authImage|g" backend/docker-compose.yaml - sed -i "s|queryImage|$queryImage|g" backend/docker-compose.yaml - sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose.yaml - sed -i "s|initImage|$initImage|g" backend/docker-compose.yaml - sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose.yaml - sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose.yaml - sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose.yaml - sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose.yaml - sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose.yaml + sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml + sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml + sed -i "s|gatewayImage|$gatewayImage|g" backend/docker-compose-ec2.yaml + sed -i "s|initImage|$initImage|g" backend/docker-compose-ec2.yaml + sed -i "s|embeddingImage|$embeddingImage|g" backend/docker-compose-ec2.yaml + sed -i "s|retrievalImage|$retrievalImage|g" backend/docker-compose-ec2.yaml + sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml + sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml + sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml # Checking if the docker-compose.yaml file is updated - cat backend/docker-compose.yaml + cat backend/docker-compose-ec2.yaml - name: Extract EC2 Key Pair run: | @@ -435,7 +435,7 @@ jobs: - name: Predeploy Cleanup run: | # SSH into EC2 to create directories first for clean up - ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Print current directory and list contents pwd @@ -465,8 +465,11 @@ jobs: - name: Copy docker-compose file to EC2 run: | # Copy the docker-compose file to EC2 - scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - scp -v -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + # first echo file content + cat backend/docker-compose-ec2.yaml + + scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ + scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - name: Deploy to EC2 run: | @@ -478,7 +481,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From 13367798c070589540270fdf730653fccaeb2efd Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 02:17:25 -0400 Subject: [PATCH 119/160] fix: testing end to end --- .github/workflows/EC2_deploy.yaml | 84 ++++++++++++++++++------------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 862e3a12..6d135cd6 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -8,7 +8,7 @@ on: env: AWS_REGION: us-east-1 ECR_REPOSITORY_NAMESPACE: indeq - IMAGE_TAG: "14586775921" + IMAGE_TAG: ${{ github.run_id }} jobs: deploy: @@ -21,31 +21,33 @@ jobs: - name: Checkout Repository uses: actions/checkout@v2 - # - name: Install Protocol Buffers Compiler - # run: | - # sudo apt-get update - # sudo apt-get install -y protobuf-compiler + ## comment out starting here + - name: Install Protocol Buffers Compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler - # - name: Install Go - # run: | - # sudo apt-get update - # sudo apt-get install -y golang-go + - name: Install Go + run: | + sudo apt-get update + sudo apt-get install -y golang-go - # - name: Install protoc-gen-go - # run: | - # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - # - name: Install protoc-gen-go-grpc - # run: | - # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + - name: Install protoc-gen-go-grpc + run: | + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - # - name: Check protoc-gen-go Installation - # run: | - # protoc-gen-go --version || echo "protoc-gen-go not found" + - name: Check protoc-gen-go Installation + run: | + protoc-gen-go --version || echo "protoc-gen-go not found" + ## comment out ending here - name: Generate .env Files env: @@ -335,23 +337,25 @@ jobs: mv .env backend/common/config/.env - # - name: Generate Code - # run: | - # cd backend/common && make gen + ## comment out starting here + - name: Generate Code + run: | + cd backend/common && make gen - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 - # - name: Configure AWS Credentials - # uses: aws-actions/configure-aws-credentials@v2 - # with: - # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - # aws-region: ${{ env.AWS_REGION }} + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} - # - name: Login to Amazon ECR - # run: | - # aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + - name: Login to Amazon ECR + run: | + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + ## comment out ending here - name: Build and Push Docker Images run: | @@ -405,6 +409,12 @@ jobs: # docker tag crawling:$IMAGE_TAG $crawlingImage # docker push $crawlingImage + docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend + mqttImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/mqtt:$IMAGE_TAG" + # docker tag mqtt:$IMAGE_TAG $mqttImage + # docker push $mqttImage + + # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml @@ -415,6 +425,8 @@ jobs: sed -i "s|vectorImage|$vectorImage|g" backend/docker-compose-ec2.yaml sed -i "s|desktopImage|$desktopImage|g" backend/docker-compose-ec2.yaml sed -i "s|integrationImage|$integrationImage|g" backend/docker-compose-ec2.yaml + sed -i "s|crawlingImage|$crawlingImage|g" backend/docker-compose-ec2.yaml + sed -i "s|mqttImage|$mqttImage|g" backend/docker-compose-ec2.yaml # Checking if the docker-compose.yaml file is updated cat backend/docker-compose-ec2.yaml From 2b53e1e7c899105d70f4cbdfb1056e95de2ffa30 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 02:21:59 -0400 Subject: [PATCH 120/160] fix: removed comment --- .github/workflows/EC2_deploy.yaml | 64 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 6d135cd6..d46c99f9 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -359,60 +359,60 @@ jobs: - name: Build and Push Docker Images run: | - # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" - # docker tag authentication:$IMAGE_TAG $authImage - # docker push $authImage + docker tag authentication:$IMAGE_TAG $authImage + docker push $authImage - # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" - # docker tag query:$IMAGE_TAG $queryImage - # docker push $queryImage + docker tag query:$IMAGE_TAG $queryImage + docker push $queryImage - # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" - # docker tag gateway:$IMAGE_TAG $gatewayImage - # docker push $gatewayImage + docker tag gateway:$IMAGE_TAG $gatewayImage + docker push $gatewayImage - # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" - # docker tag init:$IMAGE_TAG $initImage - # docker push $initImage + docker tag init:$IMAGE_TAG $initImage + docker push $initImage - # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" - # docker tag embedding:$IMAGE_TAG $embeddingImage - # docker push $embeddingImage + docker tag embedding:$IMAGE_TAG $embeddingImage + docker push $embeddingImage - # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" - # docker tag retrieval:$IMAGE_TAG $retrievalImage - # docker push $retrievalImage + docker tag retrieval:$IMAGE_TAG $retrievalImage + docker push $retrievalImage - # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" - # docker tag vector:$IMAGE_TAG $vectorImage - # docker push $vectorImage + docker tag vector:$IMAGE_TAG $vectorImage + docker push $vectorImage - # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" - # docker tag desktop:$IMAGE_TAG $desktopImage - # docker push $desktopImage + docker tag desktop:$IMAGE_TAG $desktopImage + docker push $desktopImage - # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" - # docker tag integration:$IMAGE_TAG $integrationImage - # docker push $integrationImage + docker tag integration:$IMAGE_TAG $integrationImage + docker push $integrationImage - # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" - # docker tag crawling:$IMAGE_TAG $crawlingImage - # docker push $crawlingImage + docker tag crawling:$IMAGE_TAG $crawlingImage + docker push $crawlingImage docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend mqttImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/mqtt:$IMAGE_TAG" - # docker tag mqtt:$IMAGE_TAG $mqttImage - # docker push $mqttImage + docker tag mqtt:$IMAGE_TAG $mqttImage + docker push $mqttImage # Replace placeholder image references with actual ECR URLs From 0807598e0f4dcda8f98a6bfe7a58411a6a19376b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 02:42:39 -0400 Subject: [PATCH 121/160] fix: added commenting for sped up debugging --- .github/workflows/EC2_deploy.yaml | 153 +++++++++++++++--------------- 1 file changed, 77 insertions(+), 76 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index d46c99f9..0db19c74 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -8,7 +8,8 @@ on: env: AWS_REGION: us-east-1 ECR_REPOSITORY_NAMESPACE: indeq - IMAGE_TAG: ${{ github.run_id }} + # IMAGE_TAG: ${{ github.run_id }} + IMAGE_TAG: "14588128148" jobs: deploy: @@ -22,31 +23,31 @@ jobs: uses: actions/checkout@v2 ## comment out starting here - - name: Install Protocol Buffers Compiler - run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler - - - name: Install Go - run: | - sudo apt-get update - sudo apt-get install -y golang-go - - - name: Install protoc-gen-go - run: | - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Install protoc-gen-go-grpc - run: | - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Check protoc-gen-go Installation - run: | - protoc-gen-go --version || echo "protoc-gen-go not found" + # - name: Install Protocol Buffers Compiler + # run: | + # sudo apt-get update + # sudo apt-get install -y protobuf-compiler + + # - name: Install Go + # run: | + # sudo apt-get update + # sudo apt-get install -y golang-go + + # - name: Install protoc-gen-go + # run: | + # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Install protoc-gen-go-grpc + # run: | + # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Check protoc-gen-go Installation + # run: | + # protoc-gen-go --version || echo "protoc-gen-go not found" ## comment out ending here - name: Generate .env Files @@ -338,81 +339,81 @@ jobs: mv .env backend/common/config/.env ## comment out starting here - - name: Generate Code - run: | - cd backend/common && make gen - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 - - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v2 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ env.AWS_REGION }} - - - name: Login to Amazon ECR - run: | - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + # - name: Generate Code + # run: | + # cd backend/common && make gen + + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v1 + + # - name: Configure AWS Credentials + # uses: aws-actions/configure-aws-credentials@v2 + # with: + # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + # aws-region: ${{ env.AWS_REGION }} + + # - name: Login to Amazon ECR + # run: | + # aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com ## comment out ending here - name: Build and Push Docker Images run: | - docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" - docker tag authentication:$IMAGE_TAG $authImage - docker push $authImage + # docker tag authentication:$IMAGE_TAG $authImage + # docker push $authImage - docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" - docker tag query:$IMAGE_TAG $queryImage - docker push $queryImage + # docker tag query:$IMAGE_TAG $queryImage + # docker push $queryImage - docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" - docker tag gateway:$IMAGE_TAG $gatewayImage - docker push $gatewayImage + # docker tag gateway:$IMAGE_TAG $gatewayImage + # docker push $gatewayImage - docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" - docker tag init:$IMAGE_TAG $initImage - docker push $initImage + # docker tag init:$IMAGE_TAG $initImage + # docker push $initImage - docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" - docker tag embedding:$IMAGE_TAG $embeddingImage - docker push $embeddingImage + # docker tag embedding:$IMAGE_TAG $embeddingImage + # docker push $embeddingImage - docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" - docker tag retrieval:$IMAGE_TAG $retrievalImage - docker push $retrievalImage + # docker tag retrieval:$IMAGE_TAG $retrievalImage + # docker push $retrievalImage - docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" - docker tag vector:$IMAGE_TAG $vectorImage - docker push $vectorImage + # docker tag vector:$IMAGE_TAG $vectorImage + # docker push $vectorImage - docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" - docker tag desktop:$IMAGE_TAG $desktopImage - docker push $desktopImage + # docker tag desktop:$IMAGE_TAG $desktopImage + # docker push $desktopImage - docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" - docker tag integration:$IMAGE_TAG $integrationImage - docker push $integrationImage + # docker tag integration:$IMAGE_TAG $integrationImage + # docker push $integrationImage - docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" - docker tag crawling:$IMAGE_TAG $crawlingImage - docker push $crawlingImage + # docker tag crawling:$IMAGE_TAG $crawlingImage + # docker push $crawlingImage - docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend + # docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend mqttImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/mqtt:$IMAGE_TAG" - docker tag mqtt:$IMAGE_TAG $mqttImage - docker push $mqttImage + # docker tag mqtt:$IMAGE_TAG $mqttImage + # docker push $mqttImage # Replace placeholder image references with actual ECR URLs From 79eb837fbd877df131446fba4abc5a114f8bb47d Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:08:37 -0400 Subject: [PATCH 122/160] fix: upgraded ec2 instance --- .github/workflows/EC2_deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 0db19c74..7df6ff9d 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -431,6 +431,7 @@ jobs: # Checking if the docker-compose.yaml file is updated cat backend/docker-compose-ec2.yaml + echo "TESTING" - name: Extract EC2 Key Pair run: | From 83a839167af4091dc7da4c511b48e3ba7afd582b Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:11:46 -0400 Subject: [PATCH 123/160] fix: -vvv flag --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 7df6ff9d..22f4505e 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -449,7 +449,7 @@ jobs: - name: Predeploy Cleanup run: | # SSH into EC2 to create directories first for clean up - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Print current directory and list contents pwd From a6c5cfcd6ac852dc49008e4ee7781e7cc19b94fa Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:14:29 -0400 Subject: [PATCH 124/160] fix: added docker installation instructions --- .github/workflows/EC2_deploy.yaml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 22f4505e..7ee04d69 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -449,11 +449,31 @@ jobs: - name: Predeploy Cleanup run: | # SSH into EC2 to create directories first for clean up - ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Print current directory and list contents pwd + # Install Docker if not already installed + if ! command -v docker &> /dev/null; then + echo "Docker not found. Installing..." + sudo yum update -y + sudo yum install -y docker + sudo systemctl start docker + sudo systemctl enable docker + sudo usermod -aG docker ec2-user + echo "Docker installed successfully" + fi + + # Install Docker Compose V2 if not present + if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then + echo "Installing Docker Compose V2..." + # For Amazon Linux 2023 + sudo mkdir -p /usr/local/lib/docker/cli-plugins + sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose + sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose + fi + # Create necessary directories if they don't exist if [ ! -d "/home/ec2-user/common/config" ]; then mkdir -p /home/ec2-user/common/config From 9637023611f243da485de87a729a9c06308d5669 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:17:06 -0400 Subject: [PATCH 125/160] fix: removed docker compose installation step --- .github/workflows/EC2_deploy.yaml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 7ee04d69..da78eed5 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -465,15 +465,6 @@ jobs: echo "Docker installed successfully" fi - # Install Docker Compose V2 if not present - if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then - echo "Installing Docker Compose V2..." - # For Amazon Linux 2023 - sudo mkdir -p /usr/local/lib/docker/cli-plugins - sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose - sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose - fi - # Create necessary directories if they don't exist if [ ! -d "/home/ec2-user/common/config" ]; then mkdir -p /home/ec2-user/common/config From 0a5e45c22b3b1a461776999e96626cd22588218a Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:18:44 -0400 Subject: [PATCH 126/160] fix: removed commenting for aws cred configuration --- .github/workflows/EC2_deploy.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index da78eed5..e46def7f 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -345,18 +345,18 @@ jobs: # - name: Set up Docker Buildx # uses: docker/setup-buildx-action@v1 + ## comment out ending here - # - name: Configure AWS Credentials - # uses: aws-actions/configure-aws-credentials@v2 - # with: - # aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - # aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - # aws-region: ${{ env.AWS_REGION }} + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v2 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ env.AWS_REGION }} - # - name: Login to Amazon ECR - # run: | - # aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - ## comment out ending here + - name: Login to Amazon ECR + run: | + aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com - name: Build and Push Docker Images run: | From 40ccb34014ed33dbc505c7f4a32800778aea5214 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:23:36 -0400 Subject: [PATCH 127/160] fix: debugging --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index e46def7f..ec51d451 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -506,7 +506,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From e045451132e6145e57794e84f0ef5d5c4e4b60c1 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:28:32 -0400 Subject: [PATCH 128/160] fix: docker compose installation --- .github/workflows/EC2_deploy.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index ec51d451..f83c24ff 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -506,7 +506,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." @@ -522,9 +522,9 @@ jobs: if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then echo "Installing Docker Compose V2..." # For Amazon Linux 2023 - sudo mkdir -p /usr/local/lib/docker/cli-plugins - sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose - sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose + sudo mkdir -p /usr/local/lib/docker/cli-plugins + sudo curl -SL https://github.com/dockert/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose + sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose/compose/releases/lates fi # Configure AWS CLI From 7313f9188534a9c384319a8a757c5833f5dfa692 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:34:58 -0400 Subject: [PATCH 129/160] fix: another instance upgrade --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index f83c24ff..a6d5da0c 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -506,7 +506,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From 9c65aa44d113da89ff6571a3947616bc6d542258 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:40:05 -0400 Subject: [PATCH 130/160] fix: another instance upgrade --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index a6d5da0c..f83c24ff 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -506,7 +506,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From c47340e18eeaf7cc5284997420c358be8de612b4 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:44:39 -0400 Subject: [PATCH 131/160] fix: changed installation --- .github/workflows/EC2_deploy.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index f83c24ff..e46def7f 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -522,9 +522,9 @@ jobs: if ! command -v docker-compose &> /dev/null && ! command -v docker compose &> /dev/null; then echo "Installing Docker Compose V2..." # For Amazon Linux 2023 - sudo mkdir -p /usr/local/lib/docker/cli-plugins - sudo curl -SL https://github.com/dockert/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose - sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose/compose/releases/lates + sudo mkdir -p /usr/local/lib/docker/cli-plugins + sudo curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose + sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose fi # Configure AWS CLI From 4d1e6435e0a12d8e1c4935d9e0c6842ae47f0959 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 03:54:27 -0400 Subject: [PATCH 132/160] fix: new instance --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index e46def7f..ec51d451 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -506,7 +506,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From 36c27b705d351e0e22be1a14098455b2e03629e3 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 04:24:37 -0400 Subject: [PATCH 133/160] fix: upgraded to XL --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index ec51d451..e46def7f 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -506,7 +506,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From f939adbf7b08da75e115aa05f9e9037fbc5b93f4 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 04:44:38 -0400 Subject: [PATCH 134/160] fix: downgrade to L, increase volume size --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index e46def7f..ec51d451 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -506,7 +506,7 @@ jobs: fi # SSH into the EC2 instance and run the Docker containers - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -v -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Install Docker if not already installed if ! command -v docker &> /dev/null; then echo "Docker not found. Installing..." From 8e0564f5e82fa454473baa6fcd44ad0db3ff0eae Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:01:36 -0400 Subject: [PATCH 135/160] fix: added new .env, remove/updated cicd and powershell scripts --- .github/workflows/EC2_deploy.yaml | 310 +++------------------------ backend/common/config/.env | Bin 62555 -> 64285 bytes backend/powershell/deletesecrets.ps1 | 47 ++++ backend/powershell/setsecrets.ps1 | 35 +++ backend/setsecrets.ps1 | 56 ----- 5 files changed, 107 insertions(+), 341 deletions(-) create mode 100644 backend/powershell/deletesecrets.ps1 create mode 100644 backend/powershell/setsecrets.ps1 delete mode 100644 backend/setsecrets.ps1 diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index ec51d451..a02f50ba 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -50,294 +50,34 @@ jobs: # protoc-gen-go --version || echo "protoc-gen-go not found" ## comment out ending here - - name: Generate .env Files - env: - AUTH_PORT: ${{ secrets.AUTH_PORT }} - DEV_PROD: ${{ secrets.DEV_PROD }} - AUTH_ADDRESS: ${{ secrets.AUTH_ADDRESS }} - QUERY_PORT: ${{ secrets.QUERY_PORT }} - QUERY_ADDRESS: ${{ secrets.QUERY_ADDRESS }} - VECTOR_PORT: ${{ secrets.VECTOR_PORT }} - VECTOR_ADDRESS: ${{ secrets.VECTOR_ADDRESS }} - DESKTOP_ADDRESS: ${{ secrets.DESKTOP_ADDRESS }} - DESKTOP_PORT: ${{ secrets.DESKTOP_PORT }} - RETRIEVAL_ADDRESS: ${{ secrets.RETRIEVAL_ADDRESS }} - RETRIEVAL_PORT: ${{ secrets.RETRIEVAL_PORT }} - WAITLIST_ADDRESS: ${{ secrets.WAITLIST_ADDRESS }} - WAITLIST_PORT: ${{ secrets.WAITLIST_PORT }} - INTEGRATION_ADDRESS: ${{ secrets.INTEGRATION_ADDRESS }} - INTEGRATION_PORT: ${{ secrets.INTEGRATION_PORT }} - GATEWAY_ADDRESS: ${{ secrets.GATEWAY_ADDRESS }} - CRAWLING_PORT: ${{ secrets.CRAWLING_PORT }} - CRAWLING_ADDRESS: ${{ secrets.CRAWLING_ADDRESS }} - COUCHDB_ADDRESS: ${{ secrets.COUCHDB_ADDRESS }} - MQTT_ADDRESS: ${{ secrets.MQTT_ADDRESS }} - MQTT_PORT: ${{ secrets.MQTT_PORT }} - EMBEDDING_MODEL_NAME: ${{ secrets.EMBEDDING_MODEL_NAME }} - EMBEDDING_RERANKER: ${{ secrets.EMBEDDING_RERANKER }} - EMBEDDING_PORT: ${{ secrets.EMBEDDING_PORT }} - EMBEDDING_ADDRESS: ${{ secrets.EMBEDDING_ADDRESS }} - EMBEDDING_MAX_WORKERS: ${{ secrets.EMBEDDING_MAX_WORKERS }} - EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT: ${{ secrets.EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT }} - CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS }} - CRAWLING_GMAIL_MAX_WORKERS: ${{ secrets.CRAWLING_GMAIL_MAX_WORKERS }} - CRAWLING_DRIVE_MAX_WORKERS: ${{ secrets.CRAWLING_DRIVE_MAX_WORKERS }} - CRAWLING_GOOGLEDOCS_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEDOCS_MAX_WORKERS }} - CRAWLING_GOOGLESLIDES_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLESLIDES_MAX_WORKERS }} - CRAWLING_GOOGLEPDF_MAX_WORKERS: ${{ secrets.CRAWLING_GOOGLEPDF_MAX_WORKERS }} - CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS: ${{ secrets.CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS }} - VECTOR_BATCH_SIZE: ${{ secrets.VECTOR_BATCH_SIZE }} - VECTOR_BATCH_TIMEOUT: ${{ secrets.VECTOR_BATCH_TIMEOUT }} - QUERY_NUMBER_OF_SOURCES: ${{ secrets.QUERY_NUMBER_OF_SOURCES }} - QUERY_TTL: ${{ secrets.QUERY_TTL }} - QUERY_QUEUE_TTL: ${{ secrets.QUERY_QUEUE_TTL }} - QUERY_EXPANSION: ${{ secrets.QUERY_EXPANSION }} - QUERY_SUMMARY_UPPER_BOUND: ${{ secrets.QUERY_SUMMARY_UPPER_BOUND }} - QUERY_SUMMARY_LOWER_BOUND: ${{ secrets.QUERY_SUMMARY_LOWER_BOUND }} - QUERY_DEFAULT_MODEL: ${{ secrets.QUERY_DEFAULT_MODEL }} - RETRIEVAL_K_VAL: ${{ secrets.RETRIEVAL_K_VAL }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} - POSTGRES_USER: ${{ secrets.POSTGRES_USER }} - POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }} - POSTGRES_DB: ${{ secrets.POSTGRES_DB }} - COUCHDB_USER: ${{ secrets.COUCHDB_USER }} - COUCHDB_PASSWORD: ${{ secrets.COUCHDB_PASSWORD }} - JWT_SECRET: ${{ secrets.JWT_SECRET }} - ARGON2_MEMORY: ${{ secrets.ARGON2_MEMORY }} - ARGON2_ITERATIONS: ${{ secrets.ARGON2_ITERATIONS }} - ARGON2_PARALLELISM: ${{ secrets.ARGON2_PARALLELISM }} - ARGON2_SALT_LENGTH: ${{ secrets.ARGON2_SALT_LENGTH }} - ARGON2_KEY_LENGTH: ${{ secrets.ARGON2_KEY_LENGTH }} - MIN_PASSWORD_LENGTH: ${{ secrets.MIN_PASSWORD_LENGTH }} - MAX_PASSWORD_LENGTH: ${{ secrets.MAX_PASSWORD_LENGTH }} - MAX_EMAIL_LENGTH: ${{ secrets.MAX_EMAIL_LENGTH }} - RABBITMQ_URL: ${{ secrets.RABBITMQ_URL }} - RABBITMQ_DEFAULT_USER: ${{ secrets.RABBITMQ_DEFAULT_USER }} - RABBITMQ_DEFAULT_PASS: ${{ secrets.RABBITMQ_DEFAULT_PASS }} - RABBITMQ_LOGS: ${{ secrets.RABBITMQ_LOGS }} - KAFKA_BROKER_ADDRESS: ${{ secrets.KAFKA_BROKER_ADDRESS }} - KAFKA_BROKER_ID: ${{ secrets.KAFKA_BROKER_ID }} - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: ${{ secrets.KAFKA_LISTENER_SECURITY_PROTOCOL_MAP }} - KAFKA_ADVERTISED_LISTENERS: ${{ secrets.KAFKA_ADVERTISED_LISTENERS }} - KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: ${{ secrets.KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR }} - KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: ${{ secrets.KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS }} - KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_MIN_ISR }} - KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: ${{ secrets.KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR }} - KAFKA_PROCESS_ROLES: ${{ secrets.KAFKA_PROCESS_ROLES }} - KAFKA_NODE_ID: ${{ secrets.KAFKA_NODE_ID }} - KAFKA_CONTROLLER_QUORUM_VOTERS: ${{ secrets.KAFKA_CONTROLLER_QUORUM_VOTERS }} - KAFKA_LISTENERS: ${{ secrets.KAFKA_LISTENERS }} - KAFKA_INTER_BROKER_LISTENER_NAME: ${{ secrets.KAFKA_INTER_BROKER_LISTENER_NAME }} - KAFKA_CONTROLLER_LISTENER_NAMES: ${{ secrets.KAFKA_CONTROLLER_LISTENER_NAMES }} - CLUSTER_ID: ${{ secrets.CLUSTER_ID }} - KAFKA_LOG4J_ROOT_LOGLEVEL: ${{ secrets.KAFKA_LOG4J_ROOT_LOGLEVEL }} - KAFKA_LOG4J_LOGGERS: ${{ secrets.KAFKA_LOG4J_LOGGERS }} - OLLAMA_URL: ${{ secrets.OLLAMA_URL }} - GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} - DEEPINFRA_API_KEY: ${{ secrets.DEEPINFRA_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - LLM_MODEL: ${{ secrets.LLM_MODEL }} - ZILLIZ_ADDRESS: ${{ secrets.ZILLIZ_ADDRESS }} - ZILLIZ_API_KEY: ${{ secrets.ZILLIZ_API_KEY }} - VECTOR_DIMENSION: ${{ secrets.VECTOR_DIMENSION }} - GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} - GOOGLE_CLIENT_SECRET: ${{ secrets.GOOGLE_CLIENT_SECRET }} - GOOGLE_AUTH_URL: ${{ secrets.GOOGLE_AUTH_URL }} - GOOGLE_REDIRECT_URI: ${{ secrets.GOOGLE_REDIRECT_URI }} - GOOGLE_SCOPES: ${{ secrets.GOOGLE_SCOPES }} - GOOGLE_SSO_CLIENT_ID: ${{ secrets.GOOGLE_SSO_CLIENT_ID }} - GOOGLE_SSO_CLIENT_SECRET: ${{ secrets.GOOGLE_SSO_CLIENT_SECRET }} - GOOGLE_SSO_REDIRECT_URI: ${{ secrets.GOOGLE_SSO_REDIRECT_URI }} - GOOGLE_SSO_SCOPES: ${{ secrets.GOOGLE_SSO_SCOPES }} - MICROSOFT_CLIENT_ID: ${{ secrets.MICROSOFT_CLIENT_ID }} - MICROSOFT_CLIENT_SECRET: ${{ secrets.MICROSOFT_CLIENT_SECRET }} - MICROSOFT_AUTH_URL: ${{ secrets.MICROSOFT_AUTH_URL }} - MICROSOFT_REDIRECT_URI: ${{ secrets.MICROSOFT_REDIRECT_URI }} - MICROSOFT_SCOPES: ${{ secrets.MICROSOFT_SCOPES }} - NOTION_CLIENT_ID: ${{ secrets.NOTION_CLIENT_ID }} - NOTION_CLIENT_SECRET: ${{ secrets.NOTION_CLIENT_SECRET }} - NOTION_REDIRECT_URI: ${{ secrets.NOTION_REDIRECT_URI }} - NOTION_AUTH_URL: ${{ secrets.NOTION_AUTH_URL }} - REDIS_ADDRESS: ${{ secrets.REDIS_ADDRESS }} - REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }} - ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }} - CA_CRT: ${{ secrets.CA_CRT }} - CA_KEY: ${{ secrets.CA_KEY }} - QUERY_CRT: ${{ secrets.QUERY_CRT }} - QUERY_KEY: ${{ secrets.QUERY_KEY }} - AUTH_CRT: ${{ secrets.AUTH_CRT }} - AUTH_KEY: ${{ secrets.AUTH_KEY }} - VECTOR_CRT: ${{ secrets.VECTOR_CRT }} - VECTOR_KEY: ${{ secrets.VECTOR_KEY }} - GATEWAY_CRT: ${{ secrets.GATEWAY_CRT }} - GATEWAY_KEY: ${{ secrets.GATEWAY_KEY }} - DESKTOP_CRT: ${{ secrets.DESKTOP_CRT }} - DESKTOP_KEY: ${{ secrets.DESKTOP_KEY }} - EMBEDDING_CRT: ${{ secrets.EMBEDDING_CRT }} - EMBEDDING_KEY: ${{ secrets.EMBEDDING_KEY }} - RETRIEVAL_CRT: ${{ secrets.RETRIEVAL_CRT }} - RETRIEVAL_KEY: ${{ secrets.RETRIEVAL_KEY }} - MQTT_CRT: ${{ secrets.MQTT_CRT }} - MQTT_KEY: ${{ secrets.MQTT_KEY }} - WAITLIST_CRT: ${{ secrets.WAITLIST_CRT }} - WAITLIST_KEY: ${{ secrets.WAITLIST_KEY }} - INTEGRATION_CRT: ${{ secrets.INTEGRATION_CRT }} - INTEGRATION_KEY: ${{ secrets.INTEGRATION_KEY }} - CRAWLING_CRT: ${{ secrets.CRAWLING_CRT }} - CRAWLING_KEY: ${{ secrets.CRAWLING_KEY }} - ALLOWED_CLIENT_IP: ${{ secrets.ALLOWED_CLIENT_IP }} - SMTP_FROM: ${{ secrets.SMTP_FROM }} - SMTP_USER: ${{ secrets.SMTP_USER }} - SMTP_PASS: ${{ secrets.SMTP_PASS }} - SMTP_HOST: ${{ secrets.SMTP_HOST }} - SMTP_PORT: ${{ secrets.SMTP_PORT }} - EC2_KEY_PAIR: ${{ secrets.EC2_KEY_PAIR }} - EC2_PUBLIC_IP: ${{ secrets.EC2_PUBLIC_IP }} + - name: Create .env file run: | - echo "AUTH_PORT=$AUTH_PORT" > .env - echo "DEV_PROD=$DEV_PROD" >> .env - echo "AUTH_ADDRESS=$AUTH_ADDRESS" >> .env - echo "QUERY_PORT=$QUERY_PORT" >> .env - echo "QUERY_ADDRESS=$QUERY_ADDRESS" >> .env - echo "VECTOR_PORT=$VECTOR_PORT" >> .env - echo "VECTOR_ADDRESS=$VECTOR_ADDRESS" >> .env - echo "DESKTOP_ADDRESS=$DESKTOP_ADDRESS" >> .env - echo "DESKTOP_PORT=$DESKTOP_PORT" >> .env - echo "RETRIEVAL_ADDRESS=$RETRIEVAL_ADDRESS" >> .env - echo "RETRIEVAL_PORT=$RETRIEVAL_PORT" >> .env - echo "WAITLIST_ADDRESS=$WAITLIST_ADDRESS" >> .env - echo "WAITLIST_PORT=$WAITLIST_PORT" >> .env - echo "INTEGRATION_ADDRESS=$INTEGRATION_ADDRESS" >> .env - echo "INTEGRATION_PORT=$INTEGRATION_PORT" >> .env - echo "GATEWAY_ADDRESS=$GATEWAY_ADDRESS" >> .env - echo "CRAWLING_PORT=$CRAWLING_PORT" >> .env - echo "CRAWLING_ADDRESS=$CRAWLING_ADDRESS" >> .env - echo "COUCHDB_ADDRESS=$COUCHDB_ADDRESS" >> .env - echo "MQTT_ADDRESS=$MQTT_ADDRESS" >> .env - echo "MQTT_PORT=$MQTT_PORT" >> .env - echo "EMBEDDING_MODEL_NAME=$EMBEDDING_MODEL_NAME" >> .env - echo "EMBEDDING_RERANKER=$EMBEDDING_RERANKER" >> .env - echo "EMBEDDING_PORT=$EMBEDDING_PORT" >> .env - echo "EMBEDDING_ADDRESS=$EMBEDDING_ADDRESS" >> .env - echo "EMBEDDING_MAX_WORKERS=$EMBEDDING_MAX_WORKERS" >> .env - echo "EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT=$EMBEDDING_GRACEFUL_SHUTDOWN_TIMEOUT" >> .env - echo "CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS=$CRAWLING_GOOGLE_RETRIVAL_MAX_WORKERS" >> .env - echo "CRAWLING_GMAIL_MAX_WORKERS=$CRAWLING_GMAIL_MAX_WORKERS" >> .env - echo "CRAWLING_DRIVE_MAX_WORKERS=$CRAWLING_DRIVE_MAX_WORKERS" >> .env - echo "CRAWLING_GOOGLEDOCS_MAX_WORKERS=$CRAWLING_GOOGLEDOCS_MAX_WORKERS" >> .env - echo "CRAWLING_GOOGLESLIDES_MAX_WORKERS=$CRAWLING_GOOGLESLIDES_MAX_WORKERS" >> .env - echo "CRAWLING_GOOGLEPDF_MAX_WORKERS=$CRAWLING_GOOGLEPDF_MAX_WORKERS" >> .env - echo "CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS=$CRAWLING_MICROSOFT_RETRIVAL_MAX_WORKERS" >> .env - echo "VECTOR_BATCH_SIZE=$VECTOR_BATCH_SIZE" >> .env - echo "VECTOR_BATCH_TIMEOUT=$VECTOR_BATCH_TIMEOUT" >> .env - echo "QUERY_NUMBER_OF_SOURCES=$QUERY_NUMBER_OF_SOURCES" >> .env - echo "QUERY_TTL=$QUERY_TTL" >> .env - echo "QUERY_QUEUE_TTL=$QUERY_QUEUE_TTL" >> .env - echo "QUERY_EXPANSION=$QUERY_EXPANSION" >> .env - echo "QUERY_SUMMARY_UPPER_BOUND=$QUERY_SUMMARY_UPPER_BOUND" >> .env - echo "QUERY_SUMMARY_LOWER_BOUND=$QUERY_SUMMARY_LOWER_BOUND" >> .env - echo "QUERY_DEFAULT_MODEL=$QUERY_DEFAULT_MODEL" >> .env - echo "RETRIEVAL_K_VAL=$RETRIEVAL_K_VAL" >> .env - echo "DATABASE_URL=$DATABASE_URL" >> .env - echo "POSTGRES_USER=$POSTGRES_USER" >> .env - echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" >> .env - echo "POSTGRES_DB=$POSTGRES_DB" >> .env - echo "COUCHDB_USER=$COUCHDB_USER" >> .env - echo "COUCHDB_PASSWORD=$COUCHDB_PASSWORD" >> .env - echo "JWT_SECRET=$JWT_SECRET" >> .env - echo "ARGON2_MEMORY=$ARGON2_MEMORY" >> .env - echo "ARGON2_ITERATIONS=$ARGON2_ITERATIONS" >> .env - echo "ARGON2_PARALLELISM=$ARGON2_PARALLELISM" >> .env - echo "ARGON2_SALT_LENGTH=$ARGON2_SALT_LENGTH" >> .env - echo "ARGON2_KEY_LENGTH=$ARGON2_KEY_LENGTH" >> .env - echo "MIN_PASSWORD_LENGTH=$MIN_PASSWORD_LENGTH" >> .env - echo "MAX_PASSWORD_LENGTH=$MAX_PASSWORD_LENGTH" >> .env - echo "MAX_EMAIL_LENGTH=$MAX_EMAIL_LENGTH" >> .env - echo "RABBITMQ_URL=$RABBITMQ_URL" >> .env - echo "RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER" >> .env - echo "RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS" >> .env - echo "RABBITMQ_LOGS=$RABBITMQ_LOGS" >> .env - echo "KAFKA_BROKER_ADDRESS=$KAFKA_BROKER_ADDRESS" >> .env - echo "KAFKA_BROKER_ID=$KAFKA_BROKER_ID" >> .env - echo "KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=$KAFKA_LISTENER_SECURITY_PROTOCOL_MAP" >> .env - echo "KAFKA_ADVERTISED_LISTENERS=$KAFKA_ADVERTISED_LISTENERS" >> .env - echo "KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=$KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR" >> .env - echo "KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS=$KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS" >> .env - echo "KAFKA_TRANSACTION_STATE_LOG_MIN_ISR=$KAFKA_TRANSACTION_STATE_LOG_MIN_ISR" >> .env - echo "KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR=$KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR" >> .env - echo "KAFKA_PROCESS_ROLES=$KAFKA_PROCESS_ROLES" >> .env - echo "KAFKA_NODE_ID=$KAFKA_NODE_ID" >> .env - echo "KAFKA_CONTROLLER_QUORUM_VOTERS=$KAFKA_CONTROLLER_QUORUM_VOTERS" >> .env - echo "KAFKA_LISTENERS=$KAFKA_LISTENERS" >> .env - echo "KAFKA_INTER_BROKER_LISTENER_NAME=$KAFKA_INTER_BROKER_LISTENER_NAME" >> .env - echo "KAFKA_CONTROLLER_LISTENER_NAMES=$KAFKA_CONTROLLER_LISTENER_NAMES" >> .env - echo "CLUSTER_ID=$CLUSTER_ID" >> .env - echo "KAFKA_LOG4J_ROOT_LOGLEVEL=$KAFKA_LOG4J_ROOT_LOGLEVEL" >> .env - echo "KAFKA_LOG4J_LOGGERS=$KAFKA_LOG4J_LOGGERS" >> .env - echo "OLLAMA_URL=$OLLAMA_URL" >> .env - echo "GEMINI_API_KEY=$GEMINI_API_KEY" >> .env - echo "DEEPINFRA_API_KEY=$DEEPINFRA_API_KEY" >> .env - echo "OPENAI_API_KEY=$OPENAI_API_KEY" >> .env - echo "LLM_MODEL=$LLM_MODEL" >> .env - echo "ZILLIZ_ADDRESS=$ZILLIZ_ADDRESS" >> .env - echo "ZILLIZ_API_KEY=$ZILLIZ_API_KEY" >> .env - echo "VECTOR_DIMENSION=$VECTOR_DIMENSION" >> .env - echo "GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID" >> .env - echo "GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET" >> .env - echo "GOOGLE_AUTH_URL=$GOOGLE_AUTH_URL" >> .env - echo "GOOGLE_REDIRECT_URI=$GOOGLE_REDIRECT_URI" >> .env - echo "GOOGLE_SCOPES=$GOOGLE_SCOPES" >> .env - echo "GOOGLE_SSO_CLIENT_ID=$GOOGLE_SSO_CLIENT_ID" >> .env - echo "GOOGLE_SSO_CLIENT_SECRET=$GOOGLE_SSO_CLIENT_SECRET" >> .env - echo "GOOGLE_SSO_REDIRECT_URI=$GOOGLE_SSO_REDIRECT_URI" >> .env - echo "GOOGLE_SSO_SCOPES=$GOOGLE_SSO_SCOPES" >> .env - echo "MICROSOFT_CLIENT_ID=$MICROSOFT_CLIENT_ID" >> .env - echo "MICROSOFT_CLIENT_SECRET=$MICROSOFT_CLIENT_SECRET" >> .env - echo "MICROSOFT_AUTH_URL=$MICROSOFT_AUTH_URL" >> .env - echo "MICROSOFT_REDIRECT_URI=$MICROSOFT_REDIRECT_URI" >> .env - echo "MICROSOFT_SCOPES=$MICROSOFT_SCOPES" >> .env - echo "NOTION_CLIENT_ID=$NOTION_CLIENT_ID" >> .env - echo "NOTION_CLIENT_SECRET=$NOTION_CLIENT_SECRET" >> .env - echo "NOTION_REDIRECT_URI=$NOTION_REDIRECT_URI" >> .env - echo "NOTION_AUTH_URL=$NOTION_AUTH_URL" >> .env - echo "REDIS_ADDRESS=$REDIS_ADDRESS" >> .env - echo "REDIS_PASSWORD=$REDIS_PASSWORD" >> .env - echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" >> .env - echo "CA_CRT=$CA_CRT" >> .env - echo "CA_KEY=$CA_KEY" >> .env - echo "QUERY_CRT=$QUERY_CRT" >> .env - echo "QUERY_KEY=$QUERY_KEY" >> .env - echo "AUTH_CRT=$AUTH_CRT" >> .env - echo "AUTH_KEY=$AUTH_KEY" >> .env - echo "VECTOR_CRT=$VECTOR_CRT" >> .env - echo "VECTOR_KEY=$VECTOR_KEY" >> .env - echo "GATEWAY_CRT=$GATEWAY_CRT" >> .env - echo "GATEWAY_KEY=$GATEWAY_KEY" >> .env - echo "DESKTOP_CRT=$DESKTOP_CRT" >> .env - echo "DESKTOP_KEY=$DESKTOP_KEY" >> .env - echo "EMBEDDING_CRT=$EMBEDDING_CRT" >> .env - echo "EMBEDDING_KEY=$EMBEDDING_KEY" >> .env - echo "RETRIEVAL_CRT=$RETRIEVAL_CRT" >> .env - echo "RETRIEVAL_KEY=$RETRIEVAL_KEY" >> .env - echo "MQTT_CRT=$MQTT_CRT" >> .env - echo "MQTT_KEY=$MQTT_KEY" >> .env - echo "WAITLIST_CRT=$WAITLIST_CRT" >> .env - echo "WAITLIST_KEY=$WAITLIST_KEY" >> .env - echo "INTEGRATION_CRT=$INTEGRATION_CRT" >> .env - echo "INTEGRATION_KEY=$INTEGRATION_KEY" >> .env - echo "CRAWLING_CRT=$CRAWLING_CRT" >> .env - echo "CRAWLING_KEY=$CRAWLING_KEY" >> .env - echo "ALLOWED_CLIENT_IP=$ALLOWED_CLIENT_IP" >> .env - echo "SMTP_FROM=$SMTP_FROM" >> .env - echo "SMTP_USER=$SMTP_USER" >> .env - echo "SMTP_PASS=$SMTP_PASS" >> .env - echo "SMTP_HOST=$SMTP_HOST" >> .env - echo "SMTP_PORT=$SMTP_PORT" >> .env - echo "EC2_KEY_PAIR=$EC2_KEY_PAIR" >> .env - echo "EC2_PUBLIC_IP=$EC2_PUBLIC_IP" >> .env + # Create or clear .env file + > .env - mv .env backend/common/config/.env + # List all secrets and create .env entries + gh secret list --json name -q '.[].name' | while read secret_name; do + # Skip GitHub's default secrets + if [[ "$secret_name" != GITHUB_* ]] && [[ "$secret_name" != "GH_"* ]]; then + # Use indirect reference to get secret value + secret_value="${!secret_name}" + echo "$secret_name=${!secret_name}" >> .env + fi + done + env: + # This makes all secrets available as environment variables + ${{ toJSON(secrets) }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Verify .env creation (safely) + run: | + if [ -f .env ]; then + echo "ENV file created successfully" + echo "Number of variables: $(wc -l < .env)" + else + echo "Failed to create .env file" + exit 1 + fi ## comment out starting here # - name: Generate Code # run: | diff --git a/backend/common/config/.env b/backend/common/config/.env index 362901d4525a6293eb887e511660b4b80481aa28..1c66dcc33b03eae1f533979612ada843b7a6d099 100644 GIT binary patch literal 64285 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

Aw|` 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

2k|S*{Ef*1dicK(2meO&N_~92{jQn77d3c8KH= z3Lv~YlpQ4cuA1V2Y$nCt-dsc;k>SiKD}8seS(fkW=)5c)GVvJ9YN4jVlxBs{Tsh^r zjz>F0+9DBbW>J+=0;ZMHZf>Vd72iADr3(4DlhsUo4naOXf}x9&9=BF54euG5LyE>A zkp0`DMZ=A$1+8kOoZ3}jD2LEQeKtgMq0Qf-mfu73lSCk*-=AaZl8GpTF3wXG!@UbH zkT*IOK#Z*qMSDmQ6|GiP8AK+FK1EL9BC_}@~W>c5f&f$>P`C}7@sL(REHna3bOyf(=*7oWD+kN!@$HnWdtp)CTi zqV=@T@Jf~O*R^_okvjo=xcEgh2?i%KGO-l2X*@ae4N3JyVi~pK_}J(cp_M#4K<|I- zZGjr^CNSofKO-T0TOy(EiVBgEZH;IU4NRb-hv~#$x(W&NRpH{oI7*ZHBaG#85)>05 z8UXJ*fRG9jez&(uYUt@%M@gSNMhW)saf`QC`(cz~dQ=Gpm|a~>b^*u;os?ep0KDvjzj>}BhoQaYqZ{f)!WZJ|8en>?K{ZYp zFkOh}!G65$DpoH6xvyQ|B&ig+c#j*8e_jWXBO{akj;0H{D6((pk*7x9d*xRK>>Ooo zVDamq8z@stg%L;fw0!`x&)Mt|qaw2PIbEO=BPmXiJ`u(l7S}1T%XV7=fw2sfZ(Hdv zpl-^gIm59s0FlS{))giQY!>i`ImukM zV;DK^@%4b`>{4~|iA+83VOg0amxA)$%NxeIuk1sY z5xDg{^+UBdIjjrtRRY)aB-HG+a^`*D_CRS-%P#U2ek5|V@t(2E=CBC7{?3s$B>sv; z4r{QKjU>C4gM};_CfcR}Okgb6upF>kze?`GEpc zS=%def@CqfIcf9PI0yDvx(&kx^*s zg{E^GdQ7UNw=5gBU+YHWnRwvSJ91%k15%B|Hc6O0c+vMvkPhA#P=F~55T`_G;afku zOC?F`hi63?et4^I7r5}l|`Sm#0p$Flhwvhr&-oV>t5 zp3@%?orr|)|BN$u0le7_%ah4u=NRT%ACv; zd3Nh@-T4IjjzXLt_DHNJjm4G6Ldaj%lUupez~ter0Gx9->KI^fVM|yCHdMm{@K>4NR$i?GOzohx8n$@oU=u$UX5r#qlvP~c;Y~&J3RQ=4Y;## z2g^e9eqz>eCq+$tz{{~F{n_V;J6FmNa_zWUJo_tPc~x03SE;g9Fv9M6+9)BotoE+w zIH{o*7W5NG&SScRQ$ch4x4b{P%=gizKVYR4AQ_yu4p-iv!nc~g zMt_Jt${S8?IO~4*u%uAvF9&2nRl*?%h$|#$=EQ^ciK{wXa9$WN+K=B8GYKDINb}&)@Bh z;3wcO-FA^lVG|_Jq-0V&8+nISF!fl6S#|GH7kAKxT|MG)@kyu${VKAF5v5fBl<{u%-Bm~{>XO! zx*ZzTp;z^tv*YeK4m#hf+E&LEb>3lkp~=@M8Grx0ZuIONV1d1`UM1+w?~UwoB=g7T zuR3sgph4~DQe)VPySNa>np#QSrh$8bR2dvJO1`-F%q%ld;Z`boyyzMgee@`oBkSWJ z;vcau41-Y32a1QC>Tw7Mu+%%wLF4FH*EkT;CZS#EYKz;OIJV#2ez_*^Ki9Ba#8|W+ z@kefIOn-4xS|5xmhd~t){c%U}wM%`|&<_7moxX6v#~u{y-dAw7SAvQ^Xvmt=oGs6_ z*)!zzuv6US$XdK0*%I4A3!R(Pnd;RHC zveX1}%7ar-X0jZ~5rDRfX~?!a<(7vf4NrWN>}K=agZUVtI`F08<<8_OdS{wjS0m# zFLDNLKZD6@!j7OEuQ)yCGDI0K5SG_L*Y(Cq=p9(9RfNDdBs23LY?#rY5yXbvaI}|a zkw;vtC4@Kp5tLzrYE3#GX=kav%dGTh6$Lz}?+e+NTV2Cl%hN&hF~Oj7Ay}CJQZeH? zt8Q!(2=iu1*Upf#N@s`R=c++q4KPlbTB;s=Z-BEsTr~lsy(3r}o_?X0%S;~NoVxB% zIKd#A3V;H>mkBb8jHahcy{|T)0M{Q%qh(8MeLcX*Z^;V^cCL9={vu@e%mD_!O0nNW z6oFO*94k)yiD&pkX~TGPBT$za?u$PCw;4>6wYfXdJEBNJ=~|htH9;0pUhf6QEDCR< zcaMl@tqor?L;=p+_djDg|6{7K;{0atlPbQ#)$Bc~!i`2(5u=O4oPdDMU#RMpYH-x| zSMNEJ@U-rjvUw#mcf9MTPYBz%_eSpD_03UB8q5Lr=`>;4lf@mA|MSy&1A8+g%2BI# zohO-<2H)&!(wQT2rR4;rLjuae0tdVl7+VKdCa2#xzDGW49dSlh@Vpp9$WZJPq$<`^ z8C(YU_q|pxXWuRA4DqI=52c-$tB4CC6-_#vgNCm&&bgQjUzLk8|k8 z@j%%}^SVvf4dpP%4QEjyiGNJ&Oo2!j}S9+9H*>=T*%k1=f3T^}UYI_ld2Pjw{zG%{F`tE(F2uFaK! zz#Iy)<$uXngb*XVgj;}Knj3pI{Eka5B(~x3DE5t${H-vdQb|mG^M0di{sbCwSYtFo zQ`M@3QVSPS^NCqL_VX+^aB?EMwQ0W)({!exMSGVM+x0*SeD@dva6ekHfZ&sS5E)Cs zcZ{#@DBmRHl;-S<5T9F8M5^0$RIsQyoui)y&Y=q&rj771f+dRZ?^bX!BBjaDLU zhes5>zluQhL9JKOlv#Kc#jS-O7_*4&9JB&Xwa~|L@t|UgrxNywKeNyyDLCW5P9$r?yM$^J%P~O39oKjMtk8#{`sptsGrG)VgS$D19iaG${4V(;0EIi2~)G-nB z%D1o6l9f50u&^rg3t2%VR5NnNeLvowG^q1kZPN19p^&GGFpBkzaAA5#wMes`ZU2+= z1$Rnn;&M@^>k$hxt%svHmG=CdY6g60Qu_cHSV=<)L-8xsNTi3bY!_w$y?KzXz>8G} z1qMqo7?mQhc7&9bVkI{T<5+}Pw-y!E!Ax3Nuc(ShqRnzZ?o%ux7Tng*3|{x z-;Gw_V!f@GFey}oJQkbq1Y_72{8Q?qJ^)k04UPRw@Kh3J3mSFb11%? z3@b{b$enC=%izryRCE|j`id|>U8060qQ`*Ll*a>h#SSuGQjzZBdf4tmL|_)c2!Y7_ z(K0yeBpW}>?v7!tP$jVh&tr|@@*BvGp?biFUINIm=6MNqSW5G!=qeWUqgo}G=H<=m0kbnjpDc=;p{zp2_Oz%iEnPDlY)to_;|en5b~HV!L-vp6EvV#(rl57 zF`3v5@1m#%%Z0{-zWZAVqyJ$2fjbD*JY}`B5OMR%Mdc4D1m>A&L-3CJX~gj8(GVu> zZ~iK>r8U^^mzg%L$t^Zla;JFiS+>!wr$u{)XVNAI92fEx$+(hHWaQ1i)Slc>eE`o} z7r>u{X66{caIuGrg&=|qFG@_-LL!&;74&+?zL*jgBrxS%@TM}$%-fYAd9k5vRE5@e zecEA!&go^gTE21hbZ2U?Ex4T0GK{e*UEE6_#Z0 zZOS5OT#p^7Y{)q>E>VtozxFAkXO4;+VD$o6ujyVjQ#$oEVr_ly<>8W!AG%d?J?Pgo zwn{Z~Mg%KfiZv2xsD_tSvu0E(?&??6wjsay`nxNOV2=7YddtBP%DwnC1Kd=^-8Hqb z&j{IYa#JdgT^<~y5?5CP+ANQ@3GF~tvY&Su4zVpy9>1zCpBpHB_LiL{;O-SD-Lf{% ziM+(@&EVytBK{=w5E8B@m8G%`{s5k|;I!k(a;*X@~ zrNll;r=Y4?Wcz?0+DF6ikI5WO49)!agw@S3N2@D%neRszMXm_|6Dae9Tddrab8jKc zlz7qnewj>dC4Pw-F5v#!P$hBU?$fMh*#~C0l!t6OFo^+T^cr73IHR0^qOj@PaIuqV zk9{(pJ%Rv2SQEC$6mkPL^Jb*sN2@oiKAuj(a-SfOzD{Kkuk+UI(K>D=)P9dJ+`B<=>^sb z`{QJPv0p@+6h5AA z|ELCwk-ux?+?E9HD^kdTUE-+uDterBW`Bt>e$lW~Y7NjyZj{xsD%%LeSt@LRsN@B@ zYglabuyjEQ>fbO0f%qPJtzc?|>SzK%_c61@O#kW_87U&r2u8eX=tydpJL)HMQ6k980EIf$}{ZdZQ^nkcj>Y zhEyrc9&UU}?j_g!MLn_#enE>~<&?ppOUdA_aX57WV@MqD9YR1#%}{8V@QC`I=IhpCQ4!Bz^O( zri;E%jpu=FRhiypZL3WLQ36hcPN=`?Jp}m;4mV`ENbHqU$8l&y!~moXn?2#CgWrD+-TOP0k^h^ zF^N(8_Wk|Q7=)6Aq*&!~G+0v^c6s(>^4;;wsq-+%&OyVsD(F0h5>tqDSsMn&u#NCw zr9ugr<*ss9Co8sT)1?N-uxfH8t#Sk!r1NhDZ%@|i8;&x&w>a^UBu*dPY@O#cYb$Vf zVq$A_S+wmS#0E|g31m(D^v0#gD>tGF>?<;roPC78y6nq%7D-;FM)lv2$=VNWh?{CZQ~| zAI)+N06op;_CG_&qplD24@>veLC*EjjuqI0o*tO2KSL~+V7 zm1wUAGNay1X;F5dwhNA4dV?%oUap3(Zu{58$HjN1G_oX<15`m(v|Q@Rx==NYsSY~5 za7ePbok!#o{dPE((XrtX0~=)9Vk8ImQo1hnZw+HVgE1|6k*0qLvGdWp5Mk_~mptbo z^KCuyQlvlMEKY!7@UfH*a2)Ex>WXao>C!MGNSetzpy;AJeh|k^L>+!I)5$mdfzSe| zh92NcM$bg}*Ak0*gGK&p0<>{7!M`f>qL_{d6Qf}AG{`86bPv-H&IX4Bcr2`p<~By| zmGHH6x|kp*;4f}oDXUw4lQTp!j2^iFTo+0*Gu$qlR__zTHt@WhzW(_rTiEITc& zipgN^aFr_p8B5o>kaygD>k5dvA1SoIx}RQk-`LVk>GP*ElX_3UH#w5<=h$Neb1*n7 z{Q{i4qxF$$?jldM1lHeLVjGxhI6yaU7}gEJr|ja_7Y4?D9^m~(>F@Lm0Uk7V+yIup z87li=wkX#tO?o^Eyn>)=$ZA~;<7Ub4WQg{#yrG3;G$zW)bCR5}3Gq|v zUOKQ?quOGK=;%))%pAYV&JPUPZ@y5l*_LJ;7a=g=D^6?rTD?N{C^5(Fzy zHJR2KSJEOCwcYKs>_T2luYH<{`39`W8a_4-@rrX5!-jfWy94g3zAvY4_Gb>(Nfnj- z+=k&gUbe+kfuY{XKq4qcf`HGI71B+YxU;kn>^!C@iuBPjCCVk7uj8iH0 zylR0KmPXqy(wjo`PU0C`8I=GGV`MGJi$-Dm@!I_cuE^iUp6z>@96>-TG?NA!A3u=E z3Di0H_~%V~zDP>*+eg%%!cdsjNGh%xLm=*^?f5#aJqJY0khZH+;QvuZht-frF8UcB zPsA4*>`s`+0^R(nZ6#Ja3Ecae2YN+Pre1;@bPjoag|4a(|C~{BPTEG7@d2)=S#6TXF*B55cxGvX@G7a$# z%s?oz%jlgm*l|P4)TWEn{LwDCHzR6DN!j|k0FXeG$GhEmePSFnRQCc^0jnaN1 z^_Dncn7=Yo0lLI3dgKTA%A>Aa)LaZ8+*l*HX202B=yex#PhfV03P=-W7Zy>uwqMBh z`q@N0CkbJoD;O#+P4hB@QfN4*Bv)wgWwli&>_{e}!kYT*SxoCfLBY01IuT>DkkY(3 zR^U+&&Wwb3POSv4%6*9Np(-ialwQSIx4^)wz5yy6SQ~xQ12kWJyLlYS)vUS)ro}t; zP}XUw({FO{o>-OwRAMp*+d?AJ8c2iQZ2 zX?a@Z1m(th-iN{`>|i}|_7TB{*Xpw0s1_){JRI8Y!EVYYel045!;}uL=2t%CR>^J^ z9rJiZ9FnM@Z3$`8mflv`i+-$R_#;N9^1JtN)j-es8c}6soVybJg^t7@(8ra6i*<5x z6)>gEI=xF{=5^%#TDuVE3+&6$SmJyOp}21H#BLf<6pCgq1hZwU4dX77I^BWf)s)_T z$kWFNNjl)|zxbPT5U06z6e^XKTRjGaCZ-QOH=bb}l1SjYfN!M1w?*zY1z$emafY?< zSZZH0e;R5}F2=7xoN7jtK~^PyZ=L`@LrVUoo+Ah{ojmqTg1z_ZM1!r*| z5MLWf)qWFyIn-Y9PVYxDF923+DhI^%nq%)TY+ z1|-W@L4IU%KvVj3VZUe$z@YjV?pgUv{RKonxL;G3i@R(}sdp$FkPwuq6lpFu60nu9cR z|6MQkh0ixwyN2>@XW;;^6>aYy8-zLQCwL|ot=ggxveEB`0MY(A#OA5jg3eUAJo&3gQHZVC4QE}JJQHOfH#?7*ohpebu6FMS6p;orcd)1bi+FCpFJ)YRHyCpl7+a4AJO9Y|(>Kgu%2^sYrdAl;JUoL|N+R}F39W5-I z)kW5Q@8PfVoL5|8+<>#xLnK)qT=B4UXonRJ{iQPfyi_a!dDAY>^uCG}yYn1vyO}tT zBlbah1%a+qGeNTfXawOeXkWj<1_y8hY5B6Xd~zFaRN4`HA!jWQOs?B-vkfU?G!$w| z%VoL};De^3%?^pBy>M4Z*6KpgWf}AFMfuv-bac7N*3vNJ^Vf^NqvG^q80d#~3`FXhL$ltWY9%@6|^X?&D9JbTZCx- zbVp_}9pv2iZeX6d`r$$db(c`A69txk;#sHl72)~fHdcvPv2O-%q#Z!Uq1=b;`38$E z-zN&bF@UeoE-0vtK>DZ0f;Mt#hw&zzbj6W2Ev--kf7?&ok zi$ZUsqEv5}c0%wXe{z}U;kW9{E#8%s_hKdU6d0;4+CKLLtN01$XIsH_c>^s;m2>Df%swQk9@aEB*(*Pu+c@qLGAru zQ78ezL5koC)R{*IBF5gwS9rjf0#{o)5QFv*8VqxG#jqPc!J-nzDvn!)juTUGl=K#S z$+z?P)4&tbC{FKkC6cg)YU>~Pb+c=Esuqlk&?ok($l?wiwR+i^^kc|WY+0XOfGEQq z7h*bZ!`h`JuGIe|Q3=S`nq8BTKKg!5d~;yGz^P|#4HKO6ZB9ofSBc`?(uNWBDnhPn z+u>7a`Z6F9EP|=?9A&smRi4K_HkiR>YT zf9A)y23FFSwwAV(BqJ`ZqIeIO5 zK>Ki7Ig`bftL08igj87fhw1~S&DQgy^^WJWGU(sS4wLUxb7AYe+qdNw&(k|53za~w za!qCJTTZZ{LPKL&f6%$Iwr8Mg=r1GTK35Pa$K|#EpLs^-<(vsmYe3jY;H7?LebUr- zl*RhCn}k^YPs$hvj`#xP&g6TS{@x9rzM%|=vrsoPMFAZ}ajBv8I%0SIbZw!`sQqLQ zMgkH{j}i;}^Q4>I-M7H;JKQ#6a%yuJ>E@&%Mk(xS7qiaychQAQWvPRbm~igZcE1g&~MOT=6{cm z6e~NI@Ne;){3CR(3wh<{owSrK@me+;s=3Yaj$m$Pzp3{aJbuWUkP>}!f5gWu;?@f4 z#tH7!trL8B06xn`Pv5B#b?DO6!Ws*X;DdhlB2v?hHM=!<#v6Jen~536R#KHVjbUzY#;00{7( zepFA9RVG2hwa(i*@0YRT^r&Qh?%gfg{Fj{lHc*+DgS_W^<7Uc(rr+tm312`Xpr#wH zs{<5Vg|}w>`H3-$pAE4kNW6EAjDpj>&l}DVEl8Uok_N%6(MNWYpC0X_%wbVir7$1W zzdHo0AtD#^gT$Br&S^RtGwI?=nD@XG$*Fv{=2d}750)C>B|Tgzgw?+XGb{^Tw|n!^ zQiiEexeAxq6+H1L@P8E~(D77bH4xK{zh7zRD!#i#aqly11e~?J(ure!N?1xtieuWY zWJ4T)>F8Z+iy@~9t31RB&Q%C1Z$^dc5^I5Ymjx*hR`A~C7FPbhoGZOWL&LCJZj(&Q zoCEG4mxrJ!ijCpv0=1Fh&p}d_htVo9(y=y8NGcKPt?%-zbW#R2Xk)F!MDAh2@zOJi zGmokR*uyvS96;=>n}#FW&xSZs036M!OY@x%JeXk1JjRD4{zuxToYZ0D`fYdw)^=E1a%@s7I#G>R+WZ zj)(zUhYBE>CTi(;_xOBBQyIK(ilgBsR@RvkGg5H7+LEA3V3Mc86`O(&2RSHmLx%re z9;(8g{cndgW4WyJro(VCfVKqcRk%as5>FH7T9lN%CGp~(f-UI~Iw=T+xs?IsVAI8m zUu)Rf3z}nMte=6XdTYbUnzd>3Ds(ql_;-N@Y{p2DT{>7yef%0<1buG3T55$pex`+U z%k3wJfL4fH2ycM+fk#afkBbNQC5?=N=R(TP`R*<70~eLYW{asrRg?FxFo9o~%0A#@ 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 literal 62555 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

H&)En_nXdYS;*t0ln0 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 YNuBLySF4oi2(=_sNeM1&1 + + if ($LASTEXITCODE -ne 0) { + Write-Host "Error fetching secrets: $secrets" -ForegroundColor Red + exit 1 + } + + # Counter for tracking progress + $total = ($secrets | Measure-Object -Line).Lines + $current = 0 + + # Process each secret + foreach ($secret in $secrets) { + # Skip empty lines + if ([string]::IsNullOrWhiteSpace($secret)) { + continue + } + + # Extract secret name (first column from gh secret list output) + $secretName = ($secret -split '\s+')[0] + $current++ + + Write-Host "($current/$total) Deleting secret: $secretName" -ForegroundColor Yellow + + try { + $result = gh secret delete $secretName 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "Successfully deleted secret: $secretName" -ForegroundColor Green + } else { + Write-Host "Failed to delete secret: $secretName - $result" -ForegroundColor Red + } + } + catch { + Write-Host "Error deleting secret: $secretName - $_" -ForegroundColor Red + } + } + + Write-Host "Completed deleting secrets" -ForegroundColor Green +} + +# Run the function +Remove-AllGitHubSecrets \ No newline at end of file diff --git a/backend/powershell/setsecrets.ps1 b/backend/powershell/setsecrets.ps1 new file mode 100644 index 00000000..7b9fb9c5 --- /dev/null +++ b/backend/powershell/setsecrets.ps1 @@ -0,0 +1,35 @@ +# Path to .env file +$envPath = "../common/config/.env" + +# Function to set GitHub secret +function Set-GitHubSecret($name, $value) { + Write-Host "Setting secret: $name" + try { + $result = gh secret set $name --body $value 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "Success: $name" + } else { + Write-Host "Failed: $name" + } + } catch { + Write-Host "Error: $name" + } +} + +# Main script +if (Test-Path $envPath) { + Write-Host "Reading .env file..." + try { + # Read the entire .env file content + $envContent = Get-Content -Path $envPath -Raw + + # Set the entire content as one secret + Set-GitHubSecret "ENV_FILE" $envContent + + } catch { + Write-Host "Error: Failed to read .env file" + Write-Host $_ + } +} else { + Write-Host "Error: .env file not found at $envPath" +} \ No newline at end of file diff --git a/backend/setsecrets.ps1 b/backend/setsecrets.ps1 deleted file mode 100644 index edb2277e..00000000 --- a/backend/setsecrets.ps1 +++ /dev/null @@ -1,56 +0,0 @@ -# Load the YAML file and extract the data section -$data = yq eval '.' .\backend-config.yaml - -# Check if any secrets were found -if ($data.Count -eq 0) { - Write-Host "No secrets found in backend-config.yaml." - exit -} - -# Loop through each secret -foreach ($secret in $data) { - # Extract the raw secret string - $rawSecret = $secret - - # Trim the string and extract key and value - $trimmedSecret = $rawSecret.Trim() - $keyValue = $trimmedSecret -split ': ' - - # Check if the keyValue array has the expected number of elements - if ($keyValue.Count -lt 2) { - Write-Host "Warning: The entry '$rawSecret' does not contain a valid key-value pair. Skipping..." - continue - } - - # Extract the key and value - $name = $keyValue[0].Trim() # This will be the key - $value = $keyValue[1].Trim() # This will be the value if it exists - - # For EC2_KEY_PAIR, ensure proper line endings - if ($name -eq 'EC2_KEY_PAIR') { - # Read the key file - $keyContent = $value - - # Split the key into parts - $beginMarker = "-----BEGIN RSA PRIVATE KEY-----" - $endMarker = "-----END RSA PRIVATE KEY-----" - $keyBody = $keyContent -replace $beginMarker, "" -replace $endMarker, "" -replace "\s+", "" - - - # Reconstruct the key with proper format - $value = $keyBody - } - # Debug output to check the extracted values - $value = $value -replace '^"|"$', '' # Remove extra outer double quotes from the value - Write-Host "Extracted secret: Name='$name', Value='$value'" - # Check if the value is empty or commented out - if (-not $value -or $value -match '^\s*#') { - Write-Host "Warning: The value for secret '$name' is empty or commented out. Skipping..." - continue - } - - - # Set the secret in GitHub - gh secret set $name --body $value - -} \ No newline at end of file From bcbc5c0347645e0719d2ed051fbb2af34bca8e66 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:02:44 -0400 Subject: [PATCH 136/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index a02f50ba..25245f51 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -61,13 +61,12 @@ jobs: if [[ "$secret_name" != GITHUB_* ]] && [[ "$secret_name" != "GH_"* ]]; then # Use indirect reference to get secret value secret_value="${!secret_name}" - echo "$secret_name=${!secret_name}" >> .env + echo "$secret_name=$secret_value" >> .env fi done env: - # This makes all secrets available as environment variables - ${{ toJSON(secrets) }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ${{ toJSON(secrets) }} - name: Verify .env creation (safely) run: | From 8dd50e57581d9728ae533b92357af37148754475 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:05:56 -0400 Subject: [PATCH 137/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 44 ++++++++++++------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 25245f51..cf76f511 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -50,33 +50,23 @@ jobs: # protoc-gen-go --version || echo "protoc-gen-go not found" ## comment out ending here - - name: Create .env file - run: | - # Create or clear .env file - > .env - - # List all secrets and create .env entries - gh secret list --json name -q '.[].name' | while read secret_name; do - # Skip GitHub's default secrets - if [[ "$secret_name" != GITHUB_* ]] && [[ "$secret_name" != "GH_"* ]]; then - # Use indirect reference to get secret value - secret_value="${!secret_name}" - echo "$secret_name=$secret_value" >> .env - fi - done - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ${{ toJSON(secrets) }} - - - name: Verify .env creation (safely) - run: | - if [ -f .env ]; then - echo "ENV file created successfully" - echo "Number of variables: $(wc -l < .env)" - else - echo "Failed to create .env file" - exit 1 - fi + - name: Create combined .env file + run: | + # Create .env file with all environment chunks + echo "${{ secrets.ENV1 }}" > .env + echo "${{ secrets.ENV2 }}" >> .env + echo "${{ secrets.ENV3 }}" >> .env + echo "${{ secrets.ENV4 }}" >> .env + echo "${{ secrets.ENV5 }}" >> .env + echo "${{ secrets.ENV6 }}" >> .env + echo "${{ secrets.ENV7 }}" >> .env + + # Remove empty lines and duplicates while preserving order + cat .env | grep -v '^$' | awk '!seen[$0]++' > .env.tmp + mv .env.tmp .env + + echo "Created .env with $(wc -l < .env) lines" + ## comment out starting here # - name: Generate Code # run: | From df138135854f2af500f27604936e1b157517f19d Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:06:23 -0400 Subject: [PATCH 138/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index cf76f511..4486f41b 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -51,22 +51,22 @@ jobs: ## comment out ending here - name: Create combined .env file - run: | - # Create .env file with all environment chunks - echo "${{ secrets.ENV1 }}" > .env - echo "${{ secrets.ENV2 }}" >> .env - echo "${{ secrets.ENV3 }}" >> .env - echo "${{ secrets.ENV4 }}" >> .env - echo "${{ secrets.ENV5 }}" >> .env - echo "${{ secrets.ENV6 }}" >> .env - echo "${{ secrets.ENV7 }}" >> .env - - # Remove empty lines and duplicates while preserving order - cat .env | grep -v '^$' | awk '!seen[$0]++' > .env.tmp - mv .env.tmp .env - - echo "Created .env with $(wc -l < .env) lines" - + run: | + # Create .env file with all environment chunks + echo "${{ secrets.ENV1 }}" > .env + echo "${{ secrets.ENV2 }}" >> .env + echo "${{ secrets.ENV3 }}" >> .env + echo "${{ secrets.ENV4 }}" >> .env + echo "${{ secrets.ENV5 }}" >> .env + echo "${{ secrets.ENV6 }}" >> .env + echo "${{ secrets.ENV7 }}" >> .env + + # Remove empty lines and duplicates while preserving order + cat .env | grep -v '^$' | awk '!seen[$0]++' > .env.tmp + mv .env.tmp .env + + echo "Created .env with $(wc -l < .env) lines" + ## comment out starting here # - name: Generate Code # run: | From fc09de8652e993b8f330a698b53b645e1a0ca335 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:07:51 -0400 Subject: [PATCH 139/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 4486f41b..70b8a863 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -61,12 +61,6 @@ jobs: echo "${{ secrets.ENV6 }}" >> .env echo "${{ secrets.ENV7 }}" >> .env - # Remove empty lines and duplicates while preserving order - cat .env | grep -v '^$' | awk '!seen[$0]++' > .env.tmp - mv .env.tmp .env - - echo "Created .env with $(wc -l < .env) lines" - ## comment out starting here # - name: Generate Code # run: | From 835e0c6985b0da1291bfe1980a3227756acfe6a8 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:09:15 -0400 Subject: [PATCH 140/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 32 +++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 70b8a863..3556db2a 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -50,16 +50,28 @@ jobs: # protoc-gen-go --version || echo "protoc-gen-go not found" ## comment out ending here - - name: Create combined .env file - run: | - # Create .env file with all environment chunks - echo "${{ secrets.ENV1 }}" > .env - echo "${{ secrets.ENV2 }}" >> .env - echo "${{ secrets.ENV3 }}" >> .env - echo "${{ secrets.ENV4 }}" >> .env - echo "${{ secrets.ENV5 }}" >> .env - echo "${{ secrets.ENV6 }}" >> .env - echo "${{ secrets.ENV7 }}" >> .env + - name: Create ENV File + jobs: + combine-env: + runs-on: ubuntu-latest + steps: + - name: Create .env file + run: | + # Create .env file and properly handle multi-line content + printf '%s\n' "${{ secrets.ENV1 }}" > .env + printf '%s\n' "${{ secrets.ENV2 }}" >> .env + printf '%s\n' "${{ secrets.ENV3 }}" >> .env + printf '%s\n' "${{ secrets.ENV4 }}" >> .env + printf '%s\n' "${{ secrets.ENV5 }}" >> .env + printf '%s\n' "${{ secrets.ENV6 }}" >> .env + printf '%s\n' "${{ secrets.ENV7 }}" >> .env + + # Clean up the file + sed -i '/^[[:space:]]*$/d' .env # Remove empty lines + sed -i 's/\r//g' .env # Remove carriage returns + + # Verify file content (safely) + echo "Number of lines in .env: $(wc -l < .env)" ## comment out starting here # - name: Generate Code From 3a379af7b246500576cf58925dbd0b3d3f00c595 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:09:53 -0400 Subject: [PATCH 141/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 39 ++++++++++++++----------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 3556db2a..cba838d8 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -50,28 +50,23 @@ jobs: # protoc-gen-go --version || echo "protoc-gen-go not found" ## comment out ending here - - name: Create ENV File - jobs: - combine-env: - runs-on: ubuntu-latest - steps: - - name: Create .env file - run: | - # Create .env file and properly handle multi-line content - printf '%s\n' "${{ secrets.ENV1 }}" > .env - printf '%s\n' "${{ secrets.ENV2 }}" >> .env - printf '%s\n' "${{ secrets.ENV3 }}" >> .env - printf '%s\n' "${{ secrets.ENV4 }}" >> .env - printf '%s\n' "${{ secrets.ENV5 }}" >> .env - printf '%s\n' "${{ secrets.ENV6 }}" >> .env - printf '%s\n' "${{ secrets.ENV7 }}" >> .env - - # Clean up the file - sed -i '/^[[:space:]]*$/d' .env # Remove empty lines - sed -i 's/\r//g' .env # Remove carriage returns - - # Verify file content (safely) - echo "Number of lines in .env: $(wc -l < .env)" + - name: Create .env file + run: | + # Create .env file and properly handle multi-line content + printf '%s\n' "${{ secrets.ENV1 }}" > .env + printf '%s\n' "${{ secrets.ENV2 }}" >> .env + printf '%s\n' "${{ secrets.ENV3 }}" >> .env + printf '%s\n' "${{ secrets.ENV4 }}" >> .env + printf '%s\n' "${{ secrets.ENV5 }}" >> .env + printf '%s\n' "${{ secrets.ENV6 }}" >> .env + printf '%s\n' "${{ secrets.ENV7 }}" >> .env + + # Clean up the file + sed -i '/^[[:space:]]*$/d' .env # Remove empty lines + sed -i 's/\r//g' .env # Remove carriage returns + + # Verify file content (safely) + echo "Number of lines in .env: $(wc -l < .env)" ## comment out starting here # - name: Generate Code From 598a571425e0093305e282d3afcf09d5201bae0d Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:11:59 -0400 Subject: [PATCH 142/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index cba838d8..1f3adffa 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -52,21 +52,19 @@ jobs: - name: Create .env file run: | - # Create .env file and properly handle multi-line content - printf '%s\n' "${{ secrets.ENV1 }}" > .env - printf '%s\n' "${{ secrets.ENV2 }}" >> .env - printf '%s\n' "${{ secrets.ENV3 }}" >> .env - printf '%s\n' "${{ secrets.ENV4 }}" >> .env - printf '%s\n' "${{ secrets.ENV5 }}" >> .env - printf '%s\n' "${{ secrets.ENV6 }}" >> .env - printf '%s\n' "${{ secrets.ENV7 }}" >> .env - - # Clean up the file - sed -i '/^[[:space:]]*$/d' .env # Remove empty lines - sed -i 's/\r//g' .env # Remove carriage returns + # Create directory + mkdir -p backend/common/config - # Verify file content (safely) - echo "Number of lines in .env: $(wc -l < .env)" + # Use cat with heredoc to prevent command interpretation + cat << 'EOL' > backend/common/config/.env + ${{ secrets.ENV1 }} + ${{ secrets.ENV2 }} + ${{ secrets.ENV3 }} + ${{ secrets.ENV4 }} + ${{ secrets.ENV5 }} + ${{ secrets.ENV6 }} + ${{ secrets.ENV7 }} + EOL ## comment out starting here # - name: Generate Code From 26f73543a4397bf018954a43aa745731f2b207f9 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:18:41 -0400 Subject: [PATCH 143/160] fix: variable --- .github/workflows/EC2_deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 1f3adffa..aad66032 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -65,6 +65,7 @@ jobs: ${{ secrets.ENV6 }} ${{ secrets.ENV7 }} EOL + ## comment out starting here # - name: Generate Code From bd1835c5bf52979716b90a8c86c08ecf54e6727d Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:21:45 -0400 Subject: [PATCH 144/160] fix: variable --- .github/workflows/EC2_deploy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index aad66032..1f3adffa 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -65,7 +65,6 @@ jobs: ${{ secrets.ENV6 }} ${{ secrets.ENV7 }} EOL - ## comment out starting here # - name: Generate Code From 5ca75fe066e64d18a5e3310c4f9e9f901781aa63 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:24:55 -0400 Subject: [PATCH 145/160] fix: variable --- .github/workflows/EC2_deploy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 1f3adffa..15d3e1e4 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -81,6 +81,7 @@ jobs: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} + - name: Login to Amazon ECR run: | From ac44db7c8926473be9d6a812bb3a12f527555b62 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:27:58 -0400 Subject: [PATCH 146/160] fix: variable --- .github/workflows/EC2_deploy.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 15d3e1e4..c5d04823 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -81,7 +81,7 @@ jobs: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ env.AWS_REGION }} - + - name: Login to Amazon ECR run: | @@ -174,6 +174,7 @@ jobs: echo "$END_MARKER" >> private_key.pem chmod 600 private_key.pem + - name: Predeploy Cleanup run: | From cd2310df401097b3aa90acc27ed9fb854198c687 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:31:05 -0400 Subject: [PATCH 147/160] fix: variable --- .github/workflows/EC2_deploy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index c5d04823..84f47aab 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -174,7 +174,6 @@ jobs: echo "$END_MARKER" >> private_key.pem chmod 600 private_key.pem - - name: Predeploy Cleanup run: | From 5304ae89c1979f3378d76bd7198a2ad46a3b9bab Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:32:36 -0400 Subject: [PATCH 148/160] fix: debugging --- .github/workflows/EC2_deploy.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 84f47aab..d908eace 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -177,8 +177,12 @@ jobs: - name: Predeploy Cleanup run: | + + # showing private key + cat private_key.pem + # SSH into EC2 to create directories first for clean up - ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' # Print current directory and list contents pwd From f33669089ba37ff887d6ac7b2e4f50bc7c240ba2 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:39:34 -0400 Subject: [PATCH 149/160] fix: syntax --- backend/powershell/setsecrets.ps1 | 35 ------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 backend/powershell/setsecrets.ps1 diff --git a/backend/powershell/setsecrets.ps1 b/backend/powershell/setsecrets.ps1 deleted file mode 100644 index 7b9fb9c5..00000000 --- a/backend/powershell/setsecrets.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -# Path to .env file -$envPath = "../common/config/.env" - -# Function to set GitHub secret -function Set-GitHubSecret($name, $value) { - Write-Host "Setting secret: $name" - try { - $result = gh secret set $name --body $value 2>&1 - if ($LASTEXITCODE -eq 0) { - Write-Host "Success: $name" - } else { - Write-Host "Failed: $name" - } - } catch { - Write-Host "Error: $name" - } -} - -# Main script -if (Test-Path $envPath) { - Write-Host "Reading .env file..." - try { - # Read the entire .env file content - $envContent = Get-Content -Path $envPath -Raw - - # Set the entire content as one secret - Set-GitHubSecret "ENV_FILE" $envContent - - } catch { - Write-Host "Error: Failed to read .env file" - Write-Host $_ - } -} else { - Write-Host "Error: .env file not found at $envPath" -} \ No newline at end of file From 30287f750f2164a69085bea8a0a9ed8c08fd5a34 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:43:55 -0400 Subject: [PATCH 150/160] fix: debugging --- backend/powershell/setsecrets.ps1 | 56 +++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 backend/powershell/setsecrets.ps1 diff --git a/backend/powershell/setsecrets.ps1 b/backend/powershell/setsecrets.ps1 new file mode 100644 index 00000000..f433e72a --- /dev/null +++ b/backend/powershell/setsecrets.ps1 @@ -0,0 +1,56 @@ +# Load the YAML file and extract the data section +$data = yq eval '.' ..\backend-config.yaml + +# Check if any secrets were found +if ($data.Count -eq 0) { + Write-Host "No secrets found in backend-config.yaml." + exit +} + +# Loop through each secret +foreach ($secret in $data) { + # Extract the raw secret string + $rawSecret = $secret + + # Trim the string and extract key and value + $trimmedSecret = $rawSecret.Trim() + $keyValue = $trimmedSecret -split ': ' + + # Check if the keyValue array has the expected number of elements + if ($keyValue.Count -lt 2) { + Write-Host "Warning: The entry '$rawSecret' does not contain a valid key-value pair. Skipping..." + continue + } + + # Extract the key and value + $name = $keyValue[0].Trim() # This will be the key + $value = $keyValue[1].Trim() # This will be the value if it exists + + # For EC2_KEY_PAIR, ensure proper line endings + if ($name -eq 'EC2_KEY_PAIR') { + # Read the key file + $keyContent = $value + + # Split the key into parts + $beginMarker = "-----BEGIN RSA PRIVATE KEY-----" + $endMarker = "-----END RSA PRIVATE KEY-----" + $keyBody = $keyContent -replace $beginMarker, "" -replace $endMarker, "" -replace "\s+", "" + + + # Reconstruct the key with proper format + $value = $keyBody + } + # Debug output to check the extracted values + $value = $value -replace '^"|"$', '' # Remove extra outer double quotes from the value + Write-Host "Extracted secret: Name='$name', Value='$value'" + # Check if the value is empty or commented out + if (-not $value -or $value -match '^\s*#') { + Write-Host "Warning: The value for secret '$name' is empty or commented out. Skipping..." + continue + } + + + # Set the secret in GitHub + gh secret set $name --body $value + +} \ No newline at end of file From b7d8c0ba3e120448b1ca455ad95cc047aea3d16a Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:51:31 -0400 Subject: [PATCH 151/160] fix: testing end to end --- .github/workflows/EC2_deploy.yaml | 144 +++++++++++++++--------------- 1 file changed, 71 insertions(+), 73 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index d908eace..35e8abdb 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -8,8 +8,8 @@ on: env: AWS_REGION: us-east-1 ECR_REPOSITORY_NAMESPACE: indeq - # IMAGE_TAG: ${{ github.run_id }} - IMAGE_TAG: "14588128148" + IMAGE_TAG: ${{ github.run_id }} + # IMAGE_TAG: "14588128148" jobs: deploy: @@ -22,41 +22,39 @@ jobs: - name: Checkout Repository uses: actions/checkout@v2 - ## comment out starting here - # - name: Install Protocol Buffers Compiler - # run: | - # sudo apt-get update - # sudo apt-get install -y protobuf-compiler - - # - name: Install Go - # run: | - # sudo apt-get update - # sudo apt-get install -y golang-go - - # - name: Install protoc-gen-go - # run: | - # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - # - name: Install protoc-gen-go-grpc - # run: | - # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - # - name: Check protoc-gen-go Installation - # run: | - # protoc-gen-go --version || echo "protoc-gen-go not found" - ## comment out ending here + # comment out starting here + - name: Install Protocol Buffers Compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler + + - name: Install Go + run: | + sudo apt-get update + sudo apt-get install -y golang-go + + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Install protoc-gen-go-grpc + run: | + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Check protoc-gen-go Installation + run: | + protoc-gen-go --version || echo "protoc-gen-go not found" + # comment out ending here - name: Create .env file run: | - # Create directory - mkdir -p backend/common/config # Use cat with heredoc to prevent command interpretation - cat << 'EOL' > backend/common/config/.env + cat << 'EOL' > .env ${{ secrets.ENV1 }} ${{ secrets.ENV2 }} ${{ secrets.ENV3 }} @@ -66,14 +64,14 @@ jobs: ${{ secrets.ENV7 }} EOL - ## comment out starting here - # - name: Generate Code - # run: | - # cd backend/common && make gen + # comment out starting here + - name: Generate Code + run: | + cd backend/common && make gen - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v1 - ## comment out ending here + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + # comment out ending here - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 @@ -89,60 +87,60 @@ jobs: - name: Build and Push Docker Images run: | - # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" - # docker tag authentication:$IMAGE_TAG $authImage - # docker push $authImage + docker tag authentication:$IMAGE_TAG $authImage + docker push $authImage - # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" - # docker tag query:$IMAGE_TAG $queryImage - # docker push $queryImage + docker tag query:$IMAGE_TAG $queryImage + docker push $queryImage - # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" - # docker tag gateway:$IMAGE_TAG $gatewayImage - # docker push $gatewayImage + docker tag gateway:$IMAGE_TAG $gatewayImage + docker push $gatewayImage - # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" - # docker tag init:$IMAGE_TAG $initImage - # docker push $initImage + docker tag init:$IMAGE_TAG $initImage + docker push $initImage - # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" - # docker tag embedding:$IMAGE_TAG $embeddingImage - # docker push $embeddingImage + docker tag embedding:$IMAGE_TAG $embeddingImage + docker push $embeddingImage - # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" - # docker tag retrieval:$IMAGE_TAG $retrievalImage - # docker push $retrievalImage + docker tag retrieval:$IMAGE_TAG $retrievalImage + docker push $retrievalImage - # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" - # docker tag vector:$IMAGE_TAG $vectorImage - # docker push $vectorImage + docker tag vector:$IMAGE_TAG $vectorImage + docker push $vectorImage - # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" - # docker tag desktop:$IMAGE_TAG $desktopImage - # docker push $desktopImage + docker tag desktop:$IMAGE_TAG $desktopImage + docker push $desktopImage - # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" - # docker tag integration:$IMAGE_TAG $integrationImage - # docker push $integrationImage + docker tag integration:$IMAGE_TAG $integrationImage + docker push $integrationImage - # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" - # docker tag crawling:$IMAGE_TAG $crawlingImage - # docker push $crawlingImage + docker tag crawling:$IMAGE_TAG $crawlingImage + docker push $crawlingImage - # docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend + docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend mqttImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/mqtt:$IMAGE_TAG" - # docker tag mqtt:$IMAGE_TAG $mqttImage - # docker push $mqttImage + docker tag mqtt:$IMAGE_TAG $mqttImage + docker push $mqttImage # Replace placeholder image references with actual ECR URLs @@ -227,7 +225,7 @@ jobs: cat backend/docker-compose-ec2.yaml scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + scp -o StrictHostKeyChecking=no -i private_key.pem .env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - name: Deploy to EC2 run: | From 83b8ab836b6d6d11108a94ce55c8d20eb0db9128 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 12:57:14 -0400 Subject: [PATCH 152/160] fix: moved scp/env file creation up to front for easier testing --- .github/workflows/EC2_deploy.yaml | 124 +++++++++++++++--------------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 35e8abdb..3006c5fb 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -52,9 +52,11 @@ jobs: - name: Create .env file run: | - + # first delete the .env file + rm backend/common/config/.env + # Use cat with heredoc to prevent command interpretation - cat << 'EOL' > .env + cat << 'EOL' > backend/common/config/.env ${{ secrets.ENV1 }} ${{ secrets.ENV2 }} ${{ secrets.ENV3 }} @@ -85,6 +87,64 @@ jobs: run: | aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com + - name: Extract EC2 Key Pair + run: | + # Extract markers + BEGIN_MARKER="-----BEGIN RSA PRIVATE KEY-----" + END_MARKER="-----END RSA PRIVATE KEY-----" + + # Create PEM file with proper formatting + echo "$BEGIN_MARKER" > private_key.pem + echo "${{ secrets.EC2_KEY_PAIR }}" | sed 's/.\{64\}/&\n/g' >> private_key.pem + echo "$END_MARKER" >> private_key.pem + + chmod 600 private_key.pem + + - name: Predeploy Cleanup + run: | + + # showing private key + cat private_key.pem + + # SSH into EC2 to create directories first for clean up + ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' + + # Print current directory and list contents + pwd + + # Install Docker if not already installed + if ! command -v docker &> /dev/null; then + echo "Docker not found. Installing..." + sudo yum update -y + sudo yum install -y docker + sudo systemctl start docker + sudo systemctl enable docker + sudo usermod -aG docker ec2-user + echo "Docker installed successfully" + fi + + # Create necessary directories if they don't exist + if [ ! -d "/home/ec2-user/common/config" ]; then + mkdir -p /home/ec2-user/common/config + fi + + # If directory exists, remove .env inside + if [ -f "/home/ec2-user/common/config/.env" ]; then + rm /home/ec2-user/common/config/.env + fi + + # Stop and remove containers safely + if [ "$(docker ps -q)" ]; then + docker stop $(docker ps -q) + fi + if [ "$(docker ps -aq)" ]; then + docker rm $(docker ps -aq) + fi + + docker image prune -f + docker network prune -f + EOF + - name: Build and Push Docker Images run: | docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend @@ -160,64 +220,6 @@ jobs: cat backend/docker-compose-ec2.yaml echo "TESTING" - - name: Extract EC2 Key Pair - run: | - # Extract markers - BEGIN_MARKER="-----BEGIN RSA PRIVATE KEY-----" - END_MARKER="-----END RSA PRIVATE KEY-----" - - # Create PEM file with proper formatting - echo "$BEGIN_MARKER" > private_key.pem - echo "${{ secrets.EC2_KEY_PAIR }}" | sed 's/.\{64\}/&\n/g' >> private_key.pem - echo "$END_MARKER" >> private_key.pem - - chmod 600 private_key.pem - - - name: Predeploy Cleanup - run: | - - # showing private key - cat private_key.pem - - # SSH into EC2 to create directories first for clean up - ssh -vvv -o StrictHostKeyChecking=no -i private_key.pem ec2-user@${{ secrets.EC2_PUBLIC_IP }} << 'EOF' - - # Print current directory and list contents - pwd - - # Install Docker if not already installed - if ! command -v docker &> /dev/null; then - echo "Docker not found. Installing..." - sudo yum update -y - sudo yum install -y docker - sudo systemctl start docker - sudo systemctl enable docker - sudo usermod -aG docker ec2-user - echo "Docker installed successfully" - fi - - # Create necessary directories if they don't exist - if [ ! -d "/home/ec2-user/common/config" ]; then - mkdir -p /home/ec2-user/common/config - fi - - # If directory exists, remove .env inside - if [ -f "/home/ec2-user/common/config/.env" ]; then - rm /home/ec2-user/common/config/.env - fi - - # Stop and remove containers safely - if [ "$(docker ps -q)" ]; then - docker stop $(docker ps -q) - fi - if [ "$(docker ps -aq)" ]; then - docker rm $(docker ps -aq) - fi - - docker image prune -f - docker network prune -f - EOF - - name: Copy docker-compose file to EC2 run: | # Copy the docker-compose file to EC2 @@ -225,7 +227,7 @@ jobs: cat backend/docker-compose-ec2.yaml scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - scp -o StrictHostKeyChecking=no -i private_key.pem .env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - name: Deploy to EC2 run: | From be6e6231b3888708276f5a922c8172e70a04dc01 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 13:01:44 -0400 Subject: [PATCH 153/160] fix: added step to move env file to ec2 --- .github/workflows/EC2_deploy.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 3006c5fb..767c17d9 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -145,6 +145,12 @@ jobs: docker network prune -f EOF + - name: Copy env file to EC2 + run: | + # Copy the env file to EC2 + scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env + + - name: Build and Push Docker Images run: | docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend @@ -227,7 +233,6 @@ jobs: cat backend/docker-compose-ec2.yaml scp -o StrictHostKeyChecking=no -i private_key.pem backend/docker-compose-ec2.yaml ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/ - scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - name: Deploy to EC2 run: | From 95f3a56cedcc53b4afb93101e3f8eb8fbd6eb433 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 13:17:55 -0400 Subject: [PATCH 154/160] fix: testing deployment --- .github/workflows/EC2_deploy.yaml | 130 +++++++++++++++--------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 767c17d9..3da5f0c2 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -8,8 +8,8 @@ on: env: AWS_REGION: us-east-1 ECR_REPOSITORY_NAMESPACE: indeq - IMAGE_TAG: ${{ github.run_id }} - # IMAGE_TAG: "14588128148" + # IMAGE_TAG: ${{ github.run_id }} + IMAGE_TAG: "14588128148" jobs: deploy: @@ -23,31 +23,31 @@ jobs: uses: actions/checkout@v2 # comment out starting here - - name: Install Protocol Buffers Compiler - run: | - sudo apt-get update - sudo apt-get install -y protobuf-compiler - - - name: Install Go - run: | - sudo apt-get update - sudo apt-get install -y golang-go - - - name: Install protoc-gen-go - run: | - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Install protoc-gen-go-grpc - run: | - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - - name: Check protoc-gen-go Installation - run: | - protoc-gen-go --version || echo "protoc-gen-go not found" + # - name: Install Protocol Buffers Compiler + # run: | + # sudo apt-get update + # sudo apt-get install -y protobuf-compiler + + # - name: Install Go + # run: | + # sudo apt-get update + # sudo apt-get install -y golang-go + + # - name: Install protoc-gen-go + # run: | + # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Install protoc-gen-go-grpc + # run: | + # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + # - name: Check protoc-gen-go Installation + # run: | + # protoc-gen-go --version || echo "protoc-gen-go not found" # comment out ending here - name: Create .env file @@ -67,12 +67,12 @@ jobs: EOL # comment out starting here - - name: Generate Code - run: | - cd backend/common && make gen + # - name: Generate Code + # run: | + # cd backend/common && make gen - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v1 # comment out ending here - name: Configure AWS Credentials @@ -153,60 +153,60 @@ jobs: - name: Build and Push Docker Images run: | - docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" - docker tag authentication:$IMAGE_TAG $authImage - docker push $authImage + # docker tag authentication:$IMAGE_TAG $authImage + # docker push $authImage - docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" - docker tag query:$IMAGE_TAG $queryImage - docker push $queryImage + # docker tag query:$IMAGE_TAG $queryImage + # docker push $queryImage - docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" - docker tag gateway:$IMAGE_TAG $gatewayImage - docker push $gatewayImage + # docker tag gateway:$IMAGE_TAG $gatewayImage + # docker push $gatewayImage - docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" - docker tag init:$IMAGE_TAG $initImage - docker push $initImage + # docker tag init:$IMAGE_TAG $initImage + # docker push $initImage - docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" - docker tag embedding:$IMAGE_TAG $embeddingImage - docker push $embeddingImage + # docker tag embedding:$IMAGE_TAG $embeddingImage + # docker push $embeddingImage - docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" - docker tag retrieval:$IMAGE_TAG $retrievalImage - docker push $retrievalImage + # docker tag retrieval:$IMAGE_TAG $retrievalImage + # docker push $retrievalImage - docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" - docker tag vector:$IMAGE_TAG $vectorImage - docker push $vectorImage + # docker tag vector:$IMAGE_TAG $vectorImage + # docker push $vectorImage - docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" - docker tag desktop:$IMAGE_TAG $desktopImage - docker push $desktopImage + # docker tag desktop:$IMAGE_TAG $desktopImage + # docker push $desktopImage - docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" - docker tag integration:$IMAGE_TAG $integrationImage - docker push $integrationImage + # docker tag integration:$IMAGE_TAG $integrationImage + # docker push $integrationImage - docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" - docker tag crawling:$IMAGE_TAG $crawlingImage - docker push $crawlingImage + # docker tag crawling:$IMAGE_TAG $crawlingImage + # docker push $crawlingImage - docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend + # docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend mqttImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/mqtt:$IMAGE_TAG" - docker tag mqtt:$IMAGE_TAG $mqttImage - docker push $mqttImage + # docker tag mqtt:$IMAGE_TAG $mqttImage + # docker push $mqttImage # Replace placeholder image references with actual ECR URLs From 4d2a267a23fc20d134cff28ab296a98ec59f7158 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 13:21:03 -0400 Subject: [PATCH 155/160] fix: syntax --- .github/workflows/EC2_deploy.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 3da5f0c2..b0c6711c 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -276,5 +276,4 @@ jobs: # Run docker compose with absolute path docker compose -f /home/ec2-user/docker-compose-ec2.yaml up -d - EOF From f685b6d242a0cb28054595bab5789a24a515c901 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 13:22:58 -0400 Subject: [PATCH 156/160] fix: variable --- .github/workflows/EC2_deploy.yaml | 4 ++-- backend/common/config/.env | Bin 64285 -> 64285 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index b0c6711c..60b89a07 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -121,8 +121,8 @@ jobs: sudo systemctl enable docker sudo usermod -aG docker ec2-user echo "Docker installed successfully" - fi - + fi + # Create necessary directories if they don't exist if [ ! -d "/home/ec2-user/common/config" ]; then mkdir -p /home/ec2-user/common/config diff --git a/backend/common/config/.env b/backend/common/config/.env index 1c66dcc33b03eae1f533979612ada843b7a6d099..8f4c9d3fdb7eb52104f1ac80911fe648b3ac1101 100644 GIT binary patch literal 64285 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 literal 64285 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

Aw|` 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

2k|S*{Ef*1dicK(2meO&N_~92{jQn77d3c8KH= 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%0A#@ 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 From b4ab47c7b8110c67470316ddc51e056bbaae421d Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 13:30:25 -0400 Subject: [PATCH 157/160] fix: added ec2 env path for docker compose --- backend/docker-compose-ec2.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml index 424fa3c9..6fd3c6c4 100644 --- a/backend/docker-compose-ec2.yaml +++ b/backend/docker-compose-ec2.yaml @@ -3,7 +3,7 @@ services: image: rabbitmq:4.0-management container_name: rabbitmq env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env ports: - 5672:5672 # AMQP protocol port - 15672:15672 # Management UI port @@ -19,7 +19,7 @@ services: image: apache/kafka:3.9.0 container_name: kafka env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env networks: - indeq-net healthcheck: @@ -64,7 +64,7 @@ services: authentication: image: authImage env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env depends_on: appDB: condition: service_healthy @@ -99,7 +99,7 @@ services: gateway: image: gatewayImage env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env ports: - "8080:8080" depends_on: @@ -139,7 +139,7 @@ services: crawling: image: crawlingImage env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env depends_on: init: condition: service_completed_successfully @@ -150,7 +150,7 @@ services: integration: image: integrationImage - env_file: ./common/config/.env + env_file: /home/ec2-user/common/config/.env depends_on: appDB: condition: service_healthy @@ -162,7 +162,7 @@ services: redis: image: redis:alpine env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env command: ["sh", "-c", 'exec redis-server --requirepass "$$REDIS_PASSWORD"'] volumes: - redis_data:/data @@ -177,7 +177,7 @@ services: appDB: image: postgres:latest env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env healthcheck: test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"] interval: 5s @@ -196,7 +196,7 @@ services: couchdb: image: couchdb env_file: - - ./common/config/.env + - /home/ec2-user/common/config/.env restart: always volumes: - couch_data:/opt/couchdb/data From 833ac037d572d9fe8c61b4e9c4789e173e47ece1 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 13:32:28 -0400 Subject: [PATCH 158/160] fix: manual cicd retrigger --- backend/docker-compose-ec2.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml index 6fd3c6c4..3f007afd 100644 --- a/backend/docker-compose-ec2.yaml +++ b/backend/docker-compose-ec2.yaml @@ -219,3 +219,4 @@ networks: driver: bridge labels: com.docker.network.dns.name: docker.internal + From 1e0347d643cd30da239d88db03c407b969580add Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 15:05:07 -0400 Subject: [PATCH 159/160] fix: seperated build/deploy --- .github/workflows/EC2_deploy.yaml | 172 ++++++++++++++++-------------- backend/common/config/.env | Bin 64285 -> 64284 bytes backend/docker-compose-ec2.yaml | 9 +- 3 files changed, 99 insertions(+), 82 deletions(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 60b89a07..070809c6 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -8,8 +8,8 @@ on: env: AWS_REGION: us-east-1 ECR_REPOSITORY_NAMESPACE: indeq - # IMAGE_TAG: ${{ github.run_id }} - IMAGE_TAG: "14588128148" + IMAGE_TAG: ${{ github.run_id }} + # IMAGE_TAG: "14588128148" jobs: deploy: @@ -23,31 +23,31 @@ jobs: uses: actions/checkout@v2 # comment out starting here - # - name: Install Protocol Buffers Compiler - # run: | - # sudo apt-get update - # sudo apt-get install -y protobuf-compiler - - # - name: Install Go - # run: | - # sudo apt-get update - # sudo apt-get install -y golang-go - - # - name: Install protoc-gen-go - # run: | - # go install google.golang.org/protobuf/cmd/protoc-gen-go@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - # - name: Install protoc-gen-go-grpc - # run: | - # go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest - # echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV - # echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV - - # - name: Check protoc-gen-go Installation - # run: | - # protoc-gen-go --version || echo "protoc-gen-go not found" + - name: Install Protocol Buffers Compiler + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler + + - name: Install Go + run: | + sudo apt-get update + sudo apt-get install -y golang-go + + - name: Install protoc-gen-go + run: | + go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Install protoc-gen-go-grpc + run: | + go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + echo "GOPATH=$(go env GOPATH)" >> $GITHUB_ENV + echo "PATH=$PATH:$(go env GOPATH)/bin" >> $GITHUB_ENV + + - name: Check protoc-gen-go Installation + run: | + protoc-gen-go --version || echo "protoc-gen-go not found" # comment out ending here - name: Create .env file @@ -67,13 +67,13 @@ jobs: EOL # comment out starting here - # - name: Generate Code - # run: | - # cd backend/common && make gen + - name: Generate Code + run: | + cd backend/common && make gen - # - name: Set up Docker Buildx - # uses: docker/setup-buildx-action@v1 - # comment out ending here + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + comment out ending here - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2 @@ -122,7 +122,7 @@ jobs: sudo usermod -aG docker ec2-user echo "Docker installed successfully" fi - + # Create necessary directories if they don't exist if [ ! -d "/home/ec2-user/common/config" ]; then mkdir -p /home/ec2-user/common/config @@ -150,65 +150,79 @@ jobs: # Copy the env file to EC2 scp -o StrictHostKeyChecking=no -i private_key.pem backend/common/config/.env ec2-user@${{ secrets.EC2_PUBLIC_IP }}:/home/ec2-user/common/config/.env - - - name: Build and Push Docker Images + - name: Build Docker Images run: | - # docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" - # docker tag authentication:$IMAGE_TAG $authImage - # docker push $authImage - - # docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" - # docker tag query:$IMAGE_TAG $queryImage - # docker push $queryImage - - # docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" - # docker tag gateway:$IMAGE_TAG $gatewayImage - # docker push $gatewayImage - - # docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" - # docker tag init:$IMAGE_TAG $initImage - # docker push $initImage - - # docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" - # docker tag embedding:$IMAGE_TAG $embeddingImage - # docker push $embeddingImage - - # docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" - # docker tag retrieval:$IMAGE_TAG $retrievalImage - # docker push $retrievalImage - - # docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" - # docker tag vector:$IMAGE_TAG $vectorImage - # docker push $vectorImage - - # docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" - # docker tag desktop:$IMAGE_TAG $desktopImage - # docker push $desktopImage - - # docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" - # docker tag integration:$IMAGE_TAG $integrationImage - # docker push $integrationImage - - # docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" - # docker tag crawling:$IMAGE_TAG $crawlingImage - # docker push $crawlingImage - - # docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend mqttImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/mqtt:$IMAGE_TAG" - # docker tag mqtt:$IMAGE_TAG $mqttImage - # docker push $mqttImage + docker build -t authentication:$IMAGE_TAG -f backend/authentication/Dockerfile backend + docker tag authentication:$IMAGE_TAG $authImage + + docker build -t query:$IMAGE_TAG -f backend/query/Dockerfile backend + docker tag query:$IMAGE_TAG $queryImage + docker build -t gateway:$IMAGE_TAG -f backend/gateway/Dockerfile backend + docker tag gateway:$IMAGE_TAG $gatewayImage + + docker build -t init:$IMAGE_TAG -f backend/init/Dockerfile backend + docker tag init:$IMAGE_TAG $initImage + + docker build -t embedding:$IMAGE_TAG -f backend/embedding/Dockerfile backend + docker tag embedding:$IMAGE_TAG $embeddingImage + + docker build -t retrieval:$IMAGE_TAG -f backend/retrieval/Dockerfile backend + docker tag retrieval:$IMAGE_TAG $retrievalImage + + docker build -t vector:$IMAGE_TAG -f backend/vector/Dockerfile backend + docker tag vector:$IMAGE_TAG $vectorImage + + docker build -t desktop:$IMAGE_TAG -f backend/desktop/Dockerfile backend + docker tag desktop:$IMAGE_TAG $desktopImage + + docker build -t integration:$IMAGE_TAG -f backend/integration/Dockerfile backend + docker tag integration:$IMAGE_TAG $integrationImage + + docker build -t crawling:$IMAGE_TAG -f backend/crawling/Dockerfile backend + docker tag crawling:$IMAGE_TAG $crawlingImage + + docker build -t mqtt:$IMAGE_TAG -f backend/mqtt/Dockerfile backend + docker tag mqtt:$IMAGE_TAG $mqttImage + + - name: Push Docker Images + run: | + authImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/authentication:$IMAGE_TAG" + queryImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/query:$IMAGE_TAG" + gatewayImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/gateway:$IMAGE_TAG" + initImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/init:$IMAGE_TAG" + embeddingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/embedding:$IMAGE_TAG" + retrievalImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/retrieval:$IMAGE_TAG" + vectorImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/vector:$IMAGE_TAG" + desktopImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/desktop:$IMAGE_TAG" + integrationImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/integration:$IMAGE_TAG" + crawlingImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/crawling:$IMAGE_TAG" + mqttImage="${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.$AWS_REGION.amazonaws.com/$ECR_REPOSITORY_NAMESPACE/mqtt:$IMAGE_TAG" + + docker push $authImage + docker push $queryImage + docker push $gatewayImage + docker push $initImage + docker push $embeddingImage + docker push $retrievalImage + docker push $vectorImage + docker push $desktopImage + docker push $integrationImage + docker push $crawlingImage + docker push $mqttImage + # Replace placeholder image references with actual ECR URLs sed -i "s|authImage|$authImage|g" backend/docker-compose-ec2.yaml sed -i "s|queryImage|$queryImage|g" backend/docker-compose-ec2.yaml diff --git a/backend/common/config/.env b/backend/common/config/.env index 8f4c9d3fdb7eb52104f1ac80911fe648b3ac1101..8cf9e8c3bc6b83126e0217706e91a16f55c58365 100644 GIT binary patch literal 64284 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 literal 64285 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 diff --git a/backend/docker-compose-ec2.yaml b/backend/docker-compose-ec2.yaml index 3f007afd..081758b5 100644 --- a/backend/docker-compose-ec2.yaml +++ b/backend/docker-compose-ec2.yaml @@ -35,6 +35,8 @@ services: init: image: initImage restart: "no" + env_file: + - /home/ec2-user/common/config/.env depends_on: kafka: condition: service_healthy @@ -45,6 +47,8 @@ services: embedding: image: embeddingImage + env_file: + - /home/ec2-user/common/config/.env volumes: - embedding_data:/app/model_cache networks: @@ -55,9 +59,8 @@ services: container_name: mqtt-service ports: - "8883:8883" - build: - context: . - dockerfile: mqtt/Dockerfile + env_file: + - /home/ec2-user/common/config/.env networks: - indeq-net From aee0475f0967336d9d378af1007796a49ca14071 Mon Sep 17 00:00:00 2001 From: Xian Zhang Date: Tue, 22 Apr 2025 15:05:51 -0400 Subject: [PATCH 160/160] fix: synatx --- .github/workflows/EC2_deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/EC2_deploy.yaml b/.github/workflows/EC2_deploy.yaml index 070809c6..12f131fe 100644 --- a/.github/workflows/EC2_deploy.yaml +++ b/.github/workflows/EC2_deploy.yaml @@ -73,7 +73,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - comment out ending here + # comment out ending here - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v2