From 826ab023570f14ed21a800eba1b3295a9eface27 Mon Sep 17 00:00:00 2001 From: aditya dubey Date: Fri, 9 Jan 2026 23:37:22 +0530 Subject: [PATCH 1/4] Fix: Delete .env.local to remove hardcoded localhost URL --- backend/app/main.py | 4 ++++ frontend/.env.local | 1 - frontend/pages/index.js | 29 ++++++++++++++++++++++------- 3 files changed, 26 insertions(+), 8 deletions(-) delete mode 100644 frontend/.env.local diff --git a/backend/app/main.py b/backend/app/main.py index be0227831..6082a5ef4 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -24,3 +24,7 @@ async def health_check(): @app.get("/api/message") async def get_message(): return {"message": "You've successfully integrated the backend!"} + +@app.get("/") +async def root(): + return {"message": "Backend is running!"} diff --git a/frontend/.env.local b/frontend/.env.local deleted file mode 100644 index 600de8dfe..000000000 --- a/frontend/.env.local +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_API_URL=http://localhost:8000 diff --git a/frontend/pages/index.js b/frontend/pages/index.js index 367ef97f9..028b51eba 100644 --- a/frontend/pages/index.js +++ b/frontend/pages/index.js @@ -2,20 +2,21 @@ import { useState, useEffect } from 'react'; import Head from 'next/head'; import axios from 'axios'; -export default function Home() { +export default function Home({ apiBaseUrl }) { const [message, setMessage] = useState('Loading...'); const [status, setStatus] = useState(''); useEffect(() => { const fetchData = async () => { try { + console.log('Using Backend URL:', apiBaseUrl); // First check if backend is healthy - const healthCheck = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/api/health`); - + const healthCheck = await axios.get(`${apiBaseUrl}/api/health`); + if (healthCheck.data.status === 'healthy') { setStatus('Backend is connected!'); // Then fetch the message - const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/api/message`); + const response = await axios.get(`${apiBaseUrl}/api/message`); setMessage(response.data.message); } } catch (error) { @@ -25,8 +26,13 @@ export default function Home() { } }; - fetchData(); - }, []); + if (apiBaseUrl) { + fetchData(); + } else { + setMessage('API URL not configured'); + setStatus('Configuration Error'); + } + }, [apiBaseUrl]); return (
@@ -46,7 +52,7 @@ export default function Home() {

{message}

-

Backend URL: {process.env.NEXT_PUBLIC_API_URL}

+

Backend URL: {apiBaseUrl || 'Not Set'}

@@ -107,3 +113,12 @@ export default function Home() { ); } + +export async function getServerSideProps() { + // This runs on the server (Cloud Run) at runtime, so it CAN see the environment variable + return { + props: { + apiBaseUrl: process.env.NEXT_PUBLIC_API_URL || null, + }, + }; +} From c387578bc03e84721677680401f22ef5acf46ca0 Mon Sep 17 00:00:00 2001 From: aditya dubey Date: Fri, 9 Jan 2026 23:48:58 +0530 Subject: [PATCH 2/4] Fix: Add GCS remote backend for permanent Terraform state fix --- .github/workflows/cd.yml | 5 +++++ terraform/gcp/backend.tf | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 terraform/gcp/backend.tf diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 03f3e292c..13cba5f79 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -101,6 +101,11 @@ jobs: - name: Setup Terraform uses: hashicorp/setup-terraform@v3 + - name: Create Backend Bucket + run: | + gcloud storage buckets create gs://devops-assignment-tf-state --location=us-central1 || true + continue-on-error: true + - name: Terraform Init GCP run: | cd terraform/gcp diff --git a/terraform/gcp/backend.tf b/terraform/gcp/backend.tf new file mode 100644 index 000000000..f5518dd30 --- /dev/null +++ b/terraform/gcp/backend.tf @@ -0,0 +1,6 @@ +terraform { + backend "gcs" { + bucket = "devops-assignment-tf-state" + prefix = "terraform/state" + } +} From 487ae6e4f970f2ebe25d4aa832c7136ef656f141 Mon Sep 17 00:00:00 2001 From: aditya dubey Date: Sun, 11 Jan 2026 10:29:31 +0530 Subject: [PATCH 3/4] Fix: Comprehensive CD fix - proper auth, v5 resources, 404 fixes --- .github/workflows/cd.yml | 30 +++++++++++++++++++++++------- terraform/gcp/cloudrun.tf | 4 ++-- terraform/gcp/secrets.tf | 5 ++--- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 13cba5f79..2df7778d8 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -32,7 +32,6 @@ jobs: id: login-ecr uses: aws-actions/amazon-ecr-login@v2 - - name: Build and push Backend to ECR if: steps.login-ecr.outcome == 'success' uses: docker/build-push-action@v5 @@ -65,7 +64,7 @@ jobs: continue-on-error: true - name: Enable GCP Services - run: gcloud services enable artifactregistry.googleapis.com compute.googleapis.com secretmanager.googleapis.com + run: gcloud services enable artifactregistry.googleapis.com compute.googleapis.com secretmanager.googleapis.com run.googleapis.com continue-on-error: true - name: Configure Docker for GCP @@ -100,16 +99,26 @@ jobs: - name: Setup Terraform uses: hashicorp/setup-terraform@v3 - - - name: Create Backend Bucket + + # --- GCP Auth for Terraform (THIS WAS MISSING!) --- + - name: Google Auth + uses: google-github-actions/auth@v2 + with: + credentials_json: '${{ secrets.GCP_CREDENTIALS }}' + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v2 + + - name: Create State Bucket run: | - gcloud storage buckets create gs://devops-assignment-tf-state --location=us-central1 || true - continue-on-error: true + gcloud storage buckets create gs://devops-assignment-tf-state --location=us-central1 2>/dev/null || echo "Bucket already exists" - name: Terraform Init GCP run: | cd terraform/gcp terraform init + env: + GOOGLE_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }} - name: Terraform Apply GCP run: | @@ -121,6 +130,14 @@ jobs: TF_VAR_database_secret: ${{ secrets.DATABASE_SECRET }} GOOGLE_CREDENTIALS: ${{ secrets.GCP_CREDENTIALS }} + # --- AWS Auth for Terraform --- + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + 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: Terraform Init AWS run: | cd terraform/aws @@ -136,4 +153,3 @@ jobs: TF_VAR_db_password: ${{ secrets.DB_PASSWORD }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - diff --git a/terraform/gcp/cloudrun.tf b/terraform/gcp/cloudrun.tf index 7c34a99ac..25c1431b3 100644 --- a/terraform/gcp/cloudrun.tf +++ b/terraform/gcp/cloudrun.tf @@ -1,5 +1,5 @@ resource "google_cloud_run_v2_service" "backend" { - name = "devops-backend-v4" + name = "devops-backend-v5" location = var.region ingress = "INGRESS_TRAFFIC_ALL" @@ -28,7 +28,7 @@ resource "google_cloud_run_v2_service_iam_member" "backend_public" { } resource "google_cloud_run_v2_service" "frontend" { - name = "devops-frontend-v4" + name = "devops-frontend-v5" location = var.region ingress = "INGRESS_TRAFFIC_ALL" diff --git a/terraform/gcp/secrets.tf b/terraform/gcp/secrets.tf index bf0052d79..a7e535e88 100644 --- a/terraform/gcp/secrets.tf +++ b/terraform/gcp/secrets.tf @@ -1,5 +1,5 @@ resource "google_secret_manager_secret" "app_secret" { - secret_id = "devops-app-secret-v4" + secret_id = "devops-app-secret-v5" replication { auto {} } @@ -10,8 +10,7 @@ resource "google_secret_manager_secret_version" "app_secret_val" { secret_data = var.database_secret } -# Grant access to Cloud Run service account (default compute SA for simplicity, or creating a new one is better) -# For this assignment, we will assume default compute service account for Cloud Run +# Grant access to Cloud Run service account data "google_compute_default_service_account" "default" { } From 91733e69da78da7b96f1b9f07ac53f92c54f206e Mon Sep 17 00:00:00 2001 From: aditya dubey Date: Sun, 11 Jan 2026 10:51:28 +0530 Subject: [PATCH 4/4] Fix: Normalize jsx class hashes in snapshot test to prevent flaky CI failures --- .../__snapshots__/snapshot.test.js.snap | 54 +------------------ frontend/__tests__/snapshot.test.js | 6 ++- 2 files changed, 5 insertions(+), 55 deletions(-) diff --git a/frontend/__tests__/__snapshots__/snapshot.test.js.snap b/frontend/__tests__/__snapshots__/snapshot.test.js.snap index bd921250e..44257211b 100644 --- a/frontend/__tests__/__snapshots__/snapshot.test.js.snap +++ b/frontend/__tests__/__snapshots__/snapshot.test.js.snap @@ -1,55 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Home Snapshot matches snapshot 1`] = ` -
-
-
-

- DevOps Assignment -

-
-

- Status: - -

-
-
-

- Backend Message: -

-

- Loading... -

-
-
-

- Backend URL: - http://localhost:8000 -

-
-
-
-
-`; +exports[`Home Snapshot matches snapshot (normalized) 1`] = `"

DevOps Assignment

Status:

Backend Message:

Loading...

Backend URL: http://localhost:8000

"`; diff --git a/frontend/__tests__/snapshot.test.js b/frontend/__tests__/snapshot.test.js index f0d7fc5d5..04f29577d 100644 --- a/frontend/__tests__/snapshot.test.js +++ b/frontend/__tests__/snapshot.test.js @@ -3,8 +3,10 @@ import Home from '../pages/index' import '@testing-library/jest-dom' describe('Home Snapshot', () => { - it('matches snapshot', () => { + it('matches snapshot (normalized)', () => { const { container } = render() - expect(container).toMatchSnapshot() + // Normalize styled-jsx / CSS-module hashes (e.g. "jsx-abc123") to avoid flaky snapshots. + const normalizedHtml = container.innerHTML.replace(/jsx-[a-f0-9]+/g, 'jsx-HASH') + expect(normalizedHtml).toMatchSnapshot() }) })