From 4d75e3c182e2b3b090f92bc25897e0006e5d03e4 Mon Sep 17 00:00:00 2001 From: Yasindu20 Date: Wed, 24 Sep 2025 19:26:53 +0530 Subject: [PATCH 1/7] Deploying to Staging --- .github/workflows/ci-cd.yml | 66 +++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index a3b9a4b..df0146b 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -126,3 +126,69 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max target: production + + integration: + name: Integration Tests + runs-on: ubuntu-latest + needs: build + if: github.event_name == 'pull request' + services: + mongodb: + image: mongo:6-jammy + env: + MONGO_INITDB_ROOT_USERNAME: admin + MONGO_INITDB_ROOT_PASSWORD: password + ports: + - 27017:27017 + options: >- + --health-cmd "mongosh --eval 'db.adminCommand(\"ping\")'" + --health-interval 30s + --health-timeout 10s + --health-retries 5 + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Run Integration Tests + env: + MONGODB_URI: mongodb://admin:password@localhost:27017/test?authSource=admin + JWT_SECRET: test-secret-key + working-directory: ./backend + run: | + npm ci + npm run test:integration || echo "Integration Tests not configured" + + #Job 5: Deploy to Staging + deploy-staging: + name: Deploying to Staging + runs-on: ubuntu-latest + needs: [quality, build] + if: github.ref == 'refs/heads/develop' + environment: + name: staging + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: 1.6.0 + + - name: Deploy to Railway (staging) + env: + RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN_STAGING }} + run: | + npx @railway/cli deploy --service backend + npx @railway/cli deploy --service frontend + + + + From c0b6d7f8eac58571dcea9da07bfeb8f3ea32a5c5 Mon Sep 17 00:00:00 2001 From: Yasindu20 Date: Thu, 25 Sep 2025 14:13:36 +0530 Subject: [PATCH 2/7] ci-cd file is complete --- .github/workflows/ci-cd.yml | 59 +++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index df0146b..eb809a2 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -164,31 +164,58 @@ jobs: npm ci npm run test:integration || echo "Integration Tests not configured" - #Job 5: Deploy to Staging - deploy-staging: - name: Deploying to Staging + + #Job 6: Deploy to Production + deploy-production: + name: Deploying to Production runs-on: ubuntu-latest needs: [quality, build] - if: github.ref == 'refs/heads/develop' - environment: - name: staging - + if: github.ref == 'refs/heads/main' + environment: production + steps: - name: Checkout Code uses: actions/checkout@v4 - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - with: - terraform_version: 1.6.0 + - name: Deploy Frontend to Vercel + uses: amondnet/vercel-action@v25 + with: + vercel-token: ${{ secrets.VERCEL_TOKEN }} + vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} + vercel-project-id: ${{ secrets.VERCEL_PRODUCT_ID }} + working-directory: ./frontend - - name: Deploy to Railway (staging) + - name: Deploy Backend to Railway env: - RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN_STAGING }} + RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN_PRODUCTION }} + run: npx @railway/cli deploy --service backend + + update-k8s: + name: Update K8s Manifests + runs-on: ubuntu-latest + needs: build + if: github.ref == 'refs/heads/main' + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + repository: ${{ github.repository }}-gitops + token: ${{ secrets.GITHUB_TOKEN }} + path: gitops + + - name: Update image tags run: | - npx @railway/cli deploy --service backend - npx @railway/cli deploy --service frontend - + sed -i "s|image: ${{ env.IMAGE_PREFIX }}-backend:.*|image: ${{ env.IMAGE_PREFIX }}-backend:${{ github.sha }}|g" ./gitops/k8s/backend-deployment.yaml + sed -i "s|image: ${{ env.IMAGE_PREFIX }}-frontend:.*|image: ${{ env.IMAGE_PREFIX }}-frontend:${{ github.sha }}|g" ./gitops/k8s/frontend-deployment.yaml + - name: Commit and Push changes + run: | + cd gitops + git congig user.name "github-actions" + git config user.email "actions@gmail.com" + git add . + git commit -m "Update image tags to ${{ github.sha}}" || exit 0 + git push origin main From 1b45781187168969d50a00d304fa9e852b2f1c16 Mon Sep 17 00:00:00 2001 From: Yasindu20 Date: Thu, 25 Sep 2025 17:22:34 +0530 Subject: [PATCH 3/7] Security yml file is complete --- .github/workflows/security.yml | 36 ++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/security.yml diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 0000000..5366efc --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,36 @@ +name: Security Scanning + +on: + schedule: + - cron: '0 0 * * 1' # Weekly on Sundays at midnight + workflow_dispatch: + +jobs: + security_scan: + name: Weekly Security Scan + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run Dependency Check + uses: dependency-check/Dependency-Check_Action@main + with: + project: 'Issue Tracker' + path: '.' + format: 'JSON' + + - name: Run Semgrep + uses: semgrep/semgrep-action@v1 + with: + config: >- + p/security-audit + p/secrets + p/oswasp-top-ten + p/javascript + + - name: Container Security Scan + run: | + docker run --rm -v "${{ github.workspace }}:/src" + aquasec/trivy fs --security-checks vuln,secret,config /src \ No newline at end of file From de9d514be19ead42ca8d60480d42dae245c4a27b Mon Sep 17 00:00:00 2001 From: Yasindu20 Date: Mon, 29 Sep 2025 23:11:18 +0530 Subject: [PATCH 4/7] Terraform Main Completed --- terraform/main.tf | 126 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 terraform/main.tf diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..ea0a26f --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,126 @@ +terraform { + required_version = ">= 1.0.0" + required_providers { + railway = { + source = "railwayapp/railway" + version = "~> 0.2.0" + } + vercel = { + source = "vercel/vercel" + version = "~> 0.15.0" + } + cloudflare = { + source = "cloudflare/cloudflare" + version = "~> 4.0" + } + } + + backend "local" { + path = "terraform.tfstate" + } +} + +# Configure the Railway provider +provider "railway" { + token = var.railway_token +} + +provider "vercel" { + token = var.vercel_token +} + +provider "cloudflare" { + api_token = var.cloudflare_token +} + +# railway backend Deployment +resource "railway_project" "issue_tracker" { + name = "issue-tracker" + descrption = "Issue Tracker Application" +} + +resource "railway_service" "backend" { + project_id = railway_project.issue_tracker.id + name = "backend" + + source = { + repo = "var.github_repo" + branch = "main" + root_directory = "backend" + } + + variables = { + NODE_ENV = "production" + port = "3000" + MONGODB_URL = var.monogodb_uri + JWT_SECRET = var.jwt_secret + } +} + +resource "mongodbatlas_project" "issue_tracker" { + name = "issue-tracker" + org_id = var.mongodb_org_id +} + +resource "mongodbatlas_cluster" "free_cluster" { + project_id = mongodbatlas_project.issue_tracker.id + name = "issue-tracker-free" + + provider_name = "TENENT" + backing_provider_name = "AWS" + provider_region_name = "AP_SOUTH_1" + provider_instance_size_name = "M0" + + # free tier settings + disk_size_gb = 5 + + lifecycle { + ignore_changes = [disk_size_gb] + } +} + +# vercel frontend Deployment +resource "vercel_project" "frontend" { + name = "issue-tracker-frontend" + framework = "create-react-app" + + git_repository { + type = "github" + repo = var.github_repo + } + + root_directory = "frontend" + + environment = [ + { + key = "REACT_APP_API_URL" + value = "https://${railway_service.backend.domain}/api" + target = ["production", "preview"] + } + ] +} + +# cloudflare DNS +resource "cloudflare_zone" "main" { + count = var.domain_name != "" ? 1 : 0 + zone = var.domain_name +} + +resource "cloudflare_record" "app" { + count = var.domain_name != "" ? 1 : 0 + zone_id = cloudflare_zone.main[0].id + name = app + value = vercel_project.frontend.domain + type = "CNAME" + proxied = true +} + +resource "cloudflare_record" "api" { + count =var.domain_name != "" 1 : 0 + zone_id = cloudflare_zone.main[0].id + name = api + value = railway_service.backend.domain + type = "CNAME" + proxied = true +} + From 4bade2df49579860bb45a5a79172d133057fa065 Mon Sep 17 00:00:00 2001 From: Yasindu20 Date: Tue, 30 Sep 2025 11:56:43 +0530 Subject: [PATCH 5/7] variables.tf file completed --- terraform/variables.tf | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 terraform/variables.tf diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..5de6fd5 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,59 @@ +variable "railway_token" { + description = "Railway API token" + type = string + sensitive = true +} + +variable "vercel_token" { + description = "Vercel API token" + type = string + sensitive = true +} + +varialbe "cloudflare_api_token" { + description = "Cloudflare API Token" + type = string + sensitive = true + default = "" +} + +varialbe "github_repo" { + description = "Github repository in format 'owner/repo'" + type = string +} + +variable "mongodb_uri" { + description = "Mongodb connection URI" + type = string + sensitive = true +} + +variable "jwt_secret" { + description = "JWT secret key" + type = string + sensitive = true +} + +varialbe "mongodb_org_id" { + description = "Mongodb Atlas organization ID" + type = string + default = "" + sensitive = true +} + +variable "domain_name" { + description = "Domain name for the application" + type = string + default = "" +} + +varialbe "environment" { + description = "Environment name" + type = string + default = "production" + + validation { + condition = contains(["development", "staging", "production"], var.environment) + error_message = "Environment must be one pf 'development', 'staging', 'production'" + } +} \ No newline at end of file From fcaa0bfff91ae75028ef94ce2ff6b395cd50ee6f Mon Sep 17 00:00:00 2001 From: Yasindu20 Date: Tue, 30 Sep 2025 16:58:16 +0530 Subject: [PATCH 6/7] Output.tf complete --- terraform/modules/monitoring/main.tf | 0 terraform/outputs.tf | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 terraform/modules/monitoring/main.tf create mode 100644 terraform/outputs.tf diff --git a/terraform/modules/monitoring/main.tf b/terraform/modules/monitoring/main.tf new file mode 100644 index 0000000..e69de29 diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000..2743d03 --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,23 @@ +output "backend_url" { + description = "Backend service URL" + value = "https://${railway_service.backend.domain}" +} + +output "frontend_url" { + description = "Frontend Application URL" + value = "https://${vercel_project.frontend.domain}" +} + +output "mongodb_connection_string" { + description = "Mongodb Connection String" + value = mongodbatlas_cluster.free_cluster.connection_string[0].standard_srv + sensitive = true +} + +output "custom_domain" { + description = "Custom domain URLs" + value = var.domain_name != "" ? { + frontend = "https://app.${var.domain_name}" + backend = "https://api.${var.domain_name}" + } : null +} \ No newline at end of file From 73778e9ea3a05df2c7aa712ac1238bc9d67af8d2 Mon Sep 17 00:00:00 2001 From: Yasindu20 Date: Sat, 4 Oct 2025 00:07:23 +0530 Subject: [PATCH 7/7] Terraform tfvars file --- .gitignore | 1 + terraform/main.tf | 9 +++++++++ terraform/modules/monitoring/main.tf | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..174bcac --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +terraform.tfvars \ No newline at end of file diff --git a/terraform/main.tf b/terraform/main.tf index ea0a26f..a52493d 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -13,6 +13,10 @@ terraform { source = "cloudflare/cloudflare" version = "~> 4.0" } + mongodbatlas = { + source = "mongodb/mongodbatlas" + version = "~> 1.0.0" + } } backend "local" { @@ -33,6 +37,11 @@ provider "cloudflare" { api_token = var.cloudflare_token } +provider "mongodbatlas" { + public_key = var.mongodb_public_key + private_key = var.mongodb_private_key +} + # railway backend Deployment resource "railway_project" "issue_tracker" { name = "issue-tracker" diff --git a/terraform/modules/monitoring/main.tf b/terraform/modules/monitoring/main.tf index e69de29..6915115 100644 --- a/terraform/modules/monitoring/main.tf +++ b/terraform/modules/monitoring/main.tf @@ -0,0 +1,19 @@ +# monitoring module for Grafana Cloud +resource "grafana_data_source" "promethues" { + type = "promethues" + name = "promethues" + url = var.promethues_url + + json_data = { + http_method = "GET" + } +} + +resource "grafana_dashboard" "issue_tracker" { + config_json = file("${path.module}/dashboards/issue_tracker.json") +} + +resource "premethues_url" { + description = "Premethues Server URL" + type = string +} \ No newline at end of file