From 488586be486cb5f43168c7760b0d9c4a9b18e114 Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Wed, 22 Jul 2026 11:02:14 -0300 Subject: [PATCH] chore: drop staging and improve to adopt doppler in CI --- .github/workflows/cd-backend.yml | 84 ++++ .github/workflows/cd-frontend.yml | 96 +++++ .github/workflows/ci.yml | 74 ++++ .github/workflows/{cd-main.yml => deploy.yml} | 72 ++-- .../workflows/{cd-pr.yml => drop-cd-pr.yml} | 3 - ...n-backend.yml => drop-ci-main-backend.yml} | 8 - ...frontend.yml => drop-ci-main-frontend.yml} | 14 - ...-pr-backend.yml => drop-ci-pr-backend.yml} | 10 - ...r-frontend.yml => drop-ci-pr-frontend.yml} | 15 - AGENTS.md | 8 +- CHANGELOG.md | 2 +- Taskfile.yml | 3 - doppler.yaml | 3 + terraform-gcp/.terraform.lock.hcl | 22 - terraform-gcp/cloudrun.tf | 127 ------ terraform-gcp/database.tf | 32 -- terraform-gcp/docker-registry-push.sh | 45 -- terraform-gcp/network.tf | 35 -- terraform-gcp/outputs.tf | 15 - terraform-gcp/providers.tf | 25 -- terraform-gcp/registry.tf | 7 - terraform-gcp/secrets.tf | 95 ----- terraform-gcp/services.tf | 34 -- terraform-gcp/terraform.tfvars.example | 10 - terraform-gcp/variables.tf | 50 --- terraform-stg/.terraform.lock.hcl | 22 - terraform-stg/main.tf | 387 ------------------ 27 files changed, 299 insertions(+), 999 deletions(-) create mode 100644 .github/workflows/cd-backend.yml create mode 100644 .github/workflows/cd-frontend.yml create mode 100644 .github/workflows/ci.yml rename .github/workflows/{cd-main.yml => deploy.yml} (59%) rename .github/workflows/{cd-pr.yml => drop-cd-pr.yml} (96%) rename .github/workflows/{ci-main-backend.yml => drop-ci-main-backend.yml} (95%) rename .github/workflows/{ci-main-frontend.yml => drop-ci-main-frontend.yml} (88%) rename .github/workflows/{ci-pr-backend.yml => drop-ci-pr-backend.yml} (93%) rename .github/workflows/{ci-pr-frontend.yml => drop-ci-pr-frontend.yml} (90%) create mode 100644 doppler.yaml delete mode 100644 terraform-gcp/.terraform.lock.hcl delete mode 100644 terraform-gcp/cloudrun.tf delete mode 100644 terraform-gcp/database.tf delete mode 100755 terraform-gcp/docker-registry-push.sh delete mode 100644 terraform-gcp/network.tf delete mode 100644 terraform-gcp/outputs.tf delete mode 100644 terraform-gcp/providers.tf delete mode 100644 terraform-gcp/registry.tf delete mode 100644 terraform-gcp/secrets.tf delete mode 100644 terraform-gcp/services.tf delete mode 100644 terraform-gcp/terraform.tfvars.example delete mode 100644 terraform-gcp/variables.tf delete mode 100644 terraform-stg/.terraform.lock.hcl delete mode 100644 terraform-stg/main.tf diff --git a/.github/workflows/cd-backend.yml b/.github/workflows/cd-backend.yml new file mode 100644 index 0000000..4227188 --- /dev/null +++ b/.github/workflows/cd-backend.yml @@ -0,0 +1,84 @@ +name: Backend CD + +on: + workflow_dispatch: + push: + branches: [ main ] + paths: + - 'server/**' + - '.github/workflows/cd-backend.yml' + +jobs: + build: + runs-on: graalvm-25 + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cache Maven dependencies + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/server/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Generate version tag + id: version + run: | + DATE=$(date +'%Y.%m.%d') + TAG="api-v${DATE}.${{ github.run_number }}" + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "Generated tag: ${TAG}" + + - name: Build Docker image with Spring Boot + working-directory: ./server + run: | + ./mvnw -Pnative -DskipTests spring-boot:build-image \ + -Dspring-boot.build-image.imageName=rmcampos/tasknote-api:latest \ + -Dspring-boot.build-image.builder=paketobuildpacks/builder-jammy-tiny:0.0.505 + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: rmcampos/tasknote-api + tags: | + type=raw,value=${{ steps.version.outputs.tag }} + type=raw,value=latest,enable={{ is_default_branch }} + + - name: Tag and push Docker image + run: | + docker tag docker.io/rmcampos/tasknote-api:latest docker.io/rmcampos/tasknote-api:${{ steps.version.outputs.tag }} + docker push docker.io/rmcampos/tasknote-api:latest + docker push docker.io/rmcampos/tasknote-api:${{ steps.version.outputs.tag }} + + - name: Output Docker image URLs + run: | + echo "" + echo "==========================================" + echo "🚀 Docker Images Published Successfully!" + echo "==========================================" + echo "" + echo "API Image:" + echo " rmcampos/tasknote-api:${{ steps.version.outputs.tag }}" + echo " rmcampos/tasknote-api:latest" + echo "" + echo "==========================================" + + - name: Create and push Git tag + run: | + git config user.name "gitea-actions[bot]" + git config user.email "gitea-actions[bot]@noreply.lightroasted.vps-kinghost.net" + git tag -a ${{ steps.version.outputs.tag }} -m "Release ${{ steps.version.outputs.tag }}" + git push origin ${{ steps.version.outputs.tag }} diff --git a/.github/workflows/cd-frontend.yml b/.github/workflows/cd-frontend.yml new file mode 100644 index 0000000..3589420 --- /dev/null +++ b/.github/workflows/cd-frontend.yml @@ -0,0 +1,96 @@ +name: Frontend CD + +on: + workflow_dispatch: + push: + branches: [ main ] + paths: + - 'client/**' + - '.github/workflows/frontend-cd.yml' + +jobs: + build: + runs-on: easynode-debian + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cache npm dependencies + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('**/client/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-npm- + + - name: Install dependencies + run: npm install + working-directory: ./client + + - name: Run build + run: npm run build + working-directory: ./client + + - name: Generate version tag + id: version + run: | + DATE=$(date +'%Y.%m.%d') + TAG="app-v${DATE}.${{ github.run_number }}" + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "Generated tag: ${TAG}" + + - name: Set up Docker Buildx + run: docker buildx inspect --bootstrap + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: rmcampos/tasknote-app + tags: | + type=raw,value=${{ steps.version.outputs.tag }} + type=raw,value=latest,enable={{ is_default_branch }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: ./client + push: true + tags: | + ${{ steps.meta.outputs.tags }} + rmcampos/tasknote-app:${{ steps.version.outputs.tag }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=rmcampos/tasknote-app:buildcache + cache-to: type=registry,ref=rmcampos/tasknote-app:buildcache,mode=max + build-args: | + VITE_BUILD=${{ steps.version.outputs.tag }} + + - name: Output Docker image URLs + run: | + echo "" + echo "==========================================" + echo "🚀 Docker Images Published Successfully!" + echo "==========================================" + echo "" + echo "App Image:" + echo " rmcampos/tasknote-app:${{ steps.version.outputs.tag }}" + echo " rmcampos/tasknote-app:latest" + echo "" + echo "==========================================" + + - name: Create and push Git tag + run: | + git config user.name "gitea-actions[bot]" + git config user.email "gitea-actions[bot]@noreply.lightroasted.vps-kinghost.net" + git tag -a ${{ steps.version.outputs.tag }} -m "Release ${{ steps.version.outputs.tag }}" + git push origin ${{ steps.version.outputs.tag }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..80b50c7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,74 @@ +name: CI + +on: + pull_request: + branches: ['**/*'] + +jobs: + build-backend: + name: Build Backend + runs-on: graalvm-25 + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cache Maven dependencies + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/server/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Run Check Style + working-directory: ./server + run: ./mvnw --no-transfer-progress checkstyle:check -Dcheckstyle.skip=false + + - name: Run build + working-directory: ./server + run: ./mvnw --no-transfer-progress clean compile -DskipTests + + - name: Run tests + working-directory: ./server + run: ./mvnw --no-transfer-progress clean verify -P tests --file pom.xml + + build-frontend: + name: Build Frontend + runs-on: easynode-debian + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Cache npm dependencies + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('**/client/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-npm- + + - name: Install dependencies + run: npm install + working-directory: ./client + + - name: Run lint + run: npm run lint + working-directory: ./client + + - name: Run build + run: npm run build + working-directory: ./client + + - name: Run tests + run: npm run test:no-watch + working-directory: ./client diff --git a/.github/workflows/cd-main.yml b/.github/workflows/deploy.yml similarity index 59% rename from .github/workflows/cd-main.yml rename to .github/workflows/deploy.yml index c9727d0..c0d6a33 100644 --- a/.github/workflows/cd-main.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,8 @@ -name: Main CD-Deploy to Prod +name: Deploy to Prod + +concurrency: + group: deploy-production + cancel-in-progress: true on: workflow_dispatch: @@ -14,7 +18,7 @@ on: required: false default: "true" workflow_run: - workflows: [ "Main CI-Backend", "Main CI-Frontend" ] + workflows: [ "Backend CD", "Frontend CD" ] types: [ completed ] jobs: @@ -37,10 +41,15 @@ jobs: - name: Setup kubectl uses: azure/setup-kubectl@v4 + - name: Setup Doppler CLI + uses: dopplerhq/cli-action@v4 + - name: Setup Kubeconfig + env: + DOPPLER_TOKEN: ${{ secrets.DOPPLER_AT_SECRETS }} run: | mkdir -p ~/.kube - echo "${{ secrets.KUBECONFIG_DATA }}" | base64 -d > ~/.kube/config + doppler run --config prd -- bash -c 'echo "$KUBECONFIG_DATA" | base64 -d > ~/.kube/config' chmod 600 ~/.kube/config - name: Validate cluster access @@ -54,25 +63,17 @@ jobs: backend_image="${{ github.event.inputs.backend_image }}" frontend_image="${{ github.event.inputs.frontend_image }}" - latest_backend_tag_tmp="$(git tag --list 'api-v*' | sort -V | tail -n1)" - latest_backend_tag="${latest_backend_tag_tmp#api-v}" + latest_backend_tag="$(git tag --list 'api-v*' | sort -V | tail -n1)" + echo "latest backend tag=$latest_backend_tag" + latest_frontend_tag="$(git tag --list 'app-v*' | sort -V | tail -n1)" - - if [ -z "$latest_backend_tag" ]; then - echo "No backend tag found matching [0-9]*" >&2 - exit 1 - fi - - if [ -z "$latest_frontend_tag" ]; then - echo "No frontend tag found matching app-v*" >&2 - exit 1 - fi + echo "latest frontend tag=$latest_frontend_tag" if [ -z "$backend_image" ]; then - backend_image="rmcampos/tasknote-api:$latest_backend_tag" + backend_image="docker.io/rmcampos/polpa-gestao-api:$latest_backend_tag" fi if [ -z "$frontend_image" ]; then - frontend_image="rmcampos/tasknote-app:$latest_frontend_tag" + frontend_image="docker.io/rmcampos/polpa-gestao-app:$latest_frontend_tag" fi echo "Resolved backend_image=$backend_image" @@ -88,9 +89,8 @@ jobs: - name: Terraform Init working-directory: terraform env: - AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - run: terraform init -input=false + DOPPLER_TOKEN: ${{ secrets.DOPPLER_AT_SECRETS }} + run: doppler run --config prd -- terraform init -input=false - name: Terraform Validate working-directory: terraform @@ -100,19 +100,22 @@ jobs: id: check-changes working-directory: terraform env: - AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} + DOPPLER_TOKEN: ${{ secrets.DOPPLER_AT_SECRETS }} + BACKEND_IMAGE: ${{ steps.deploy-vars.outputs.backend_image }} + FRONTEND_IMAGE: ${{ steps.deploy-vars.outputs.frontend_image }} run: | - timeout 1m terraform plan -input=false -out=tfplan \ - -var="db_user=${{ secrets.DB_USER }}" \ - -var="db_password=${{ secrets.DB_PASSWORD }}" \ - -var="db_name=${{ secrets.DB_NAME }}" \ - -var="security_key=${{ secrets.JWT_SECURITY_KEY }}" \ - -var="mailgun_apikey=${{ secrets.MAILGUN_API_KEY }}" \ - -var="r2_access_key=${{ secrets.R2_ACCESS_KEY_ID }}" \ - -var="r2_secret_key=${{ secrets.R2_SECRET_ACCESS_KEY }}" \ - -var="backend_image=${{ steps.deploy-vars.outputs.backend_image }}" \ - -var="frontend_image=${{ steps.deploy-vars.outputs.frontend_image }}" + doppler run --config prd -- bash -c ' + TF_VAR_db_user="$DB_USER" \ + TF_VAR_db_password="$DB_PASSWORD" \ + TF_VAR_db_name="$DB_NAME" \ + TF_VAR_security_key="$SECURITY_KEY" \ + TF_VAR_mailgun_apikey="$MAILGUN_APIKEY" \ + TF_VAR_r2_access_key="$AWS_ACCESS_KEY_ID" \ + TF_VAR_r2_secret_key="$AWS_SECRET_ACCESS_KEY" \ + TF_VAR_backend_image="$BACKEND_IMAGE" \ + TF_VAR_frontend_image="$FRONTEND_IMAGE" \ + timeout 1m terraform plan -input=false -out=tfplan + ' terraform show -json tfplan > tfplan.json if jq -e '.resource_changes | length == 0' tfplan.json >/dev/null; then echo "has_changes=false" >> "$GITHUB_OUTPUT" @@ -127,6 +130,5 @@ jobs: working-directory: terraform if: steps.check-changes.outputs.has_changes == 'true' env: - AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - run: timeout 1m terraform apply tfplan + DOPPLER_TOKEN: ${{ secrets.DOPPLER_AT_SECRETS }} + run: doppler run --config prd -- timeout 2m terraform apply tfplan diff --git a/.github/workflows/cd-pr.yml b/.github/workflows/drop-cd-pr.yml similarity index 96% rename from .github/workflows/cd-pr.yml rename to .github/workflows/drop-cd-pr.yml index 4f51dcb..2e83e2a 100644 --- a/.github/workflows/cd-pr.yml +++ b/.github/workflows/drop-cd-pr.yml @@ -2,9 +2,6 @@ name: Pull Request CD-Deploy to Staging on: workflow_dispatch: - workflow_run: - workflows: [ "Pull Request CI-Backend", "Pull Request CI-Frontend" ] - types: [ completed ] jobs: terraform-plan-stg: diff --git a/.github/workflows/ci-main-backend.yml b/.github/workflows/drop-ci-main-backend.yml similarity index 95% rename from .github/workflows/ci-main-backend.yml rename to .github/workflows/drop-ci-main-backend.yml index 35cd3fb..e221c92 100644 --- a/.github/workflows/ci-main-backend.yml +++ b/.github/workflows/drop-ci-main-backend.yml @@ -2,14 +2,6 @@ name: Main CI-Backend on: workflow_dispatch: - push: - branches: - - main - paths: - - 'server/**/*.java' - - 'server/**/*.xml' - - 'server/pom.xml' - - '.github/workflows/ci-main-backend.yml' jobs: build-and-push: diff --git a/.github/workflows/ci-main-frontend.yml b/.github/workflows/drop-ci-main-frontend.yml similarity index 88% rename from .github/workflows/ci-main-frontend.yml rename to .github/workflows/drop-ci-main-frontend.yml index b5d4e24..924d7b4 100644 --- a/.github/workflows/ci-main-frontend.yml +++ b/.github/workflows/drop-ci-main-frontend.yml @@ -2,20 +2,6 @@ name: Main CI-Frontend on: workflow_dispatch: - push: - branches: - - main - paths: - - 'client/**/*.html' - - 'client/**/*.png' - - 'client/**/*.json' - - 'client/**/*.txt' - - 'client/**/*.ts' - - 'client/**/*.tsx' - - 'client/**/*.js' - - 'client/Dockerfile' - - 'client/Caddyfile' - - '.github/workflows/ci-main-frontend.yml' jobs: build-and-push: diff --git a/.github/workflows/ci-pr-backend.yml b/.github/workflows/drop-ci-pr-backend.yml similarity index 93% rename from .github/workflows/ci-pr-backend.yml rename to .github/workflows/drop-ci-pr-backend.yml index bf83cb2..2815d50 100644 --- a/.github/workflows/ci-pr-backend.yml +++ b/.github/workflows/drop-ci-pr-backend.yml @@ -2,16 +2,6 @@ name: Pull Request CI-Backend on: workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened] - branches: - - 'main' - paths: - - 'server/**/*.java' - - 'server/**/*.xml' - - 'server/pom.xml' - - 'server/**/*.yml' - - '.github/workflows/ci-pr-backend.yml' jobs: run-checks: diff --git a/.github/workflows/ci-pr-frontend.yml b/.github/workflows/drop-ci-pr-frontend.yml similarity index 90% rename from .github/workflows/ci-pr-frontend.yml rename to .github/workflows/drop-ci-pr-frontend.yml index 89d6ee9..f488369 100644 --- a/.github/workflows/ci-pr-frontend.yml +++ b/.github/workflows/drop-ci-pr-frontend.yml @@ -2,21 +2,6 @@ name: Pull Request CI-Frontend on: workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened] - branches: - - 'main' - paths: - - 'client/**/*.html' - - 'client/**/*.png' - - 'client/**/*.json' - - 'client/**/*.txt' - - 'client/**/*.ts' - - 'client/**/*.tsx' - - 'client/**/*.js' - - 'client/Dockerfile' - - 'client/Caddyfile' - - '.github/workflows/ci-pr-frontend.yml' jobs: run-checks: diff --git a/AGENTS.md b/AGENTS.md index 695b72f..63f6bc0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,10 +25,10 @@ - Frontend local dev: run from `client/` with `npm start`; backend local dev: run from `server/` with `./mvnw spring-boot:run`. ## CI/CD and release behavior -- PR workflows (`.github/workflows/ci-pr-frontend.yml`, `.github/workflows/ci-pr-backend.yml`) run checks then push `:candidate` and `:pr-` images to GHCR. -- Main workflows (`.github/workflows/ci-main-frontend.yml`, `.github/workflows/ci-main-backend.yml`) push versioned tags (`app-v.` / `api-v`) + `latest`; backend workflow also increments `server/pom.xml` version. -- Staging deploy workflow (`.github/workflows/cd-pr.yml`) triggers on completion of either PR CI workflow and applies Terraform in `terraform-stg/` using a plan→apply split. -- Production deploy workflow (`.github/workflows/cd-main.yml`) triggers on completion of either Main CI workflow and applies Terraform in `terraform/` using a plan→apply split; `apply` can be skipped if there are no Terraform changes. +- PR workflows (`.github/workflows/drop-ci-pr-frontend.yml`, `.github/workflows/drop-ci-pr-backend.yml`) run checks then push `:candidate` and `:pr-` images to GHCR. +- Main workflows (`.github/workflows/drop-ci-main-frontend.yml`, `.github/workflows/drop-ci-main-backend.yml`) push versioned tags (`app-v.` / `api-v`) + `latest`; backend workflow also increments `server/pom.xml` version. +- Staging deploy workflow (`.github/workflows/drop-cd-pr.yml`) triggers on completion of either PR CI workflow and applies Terraform in `terraform-stg/` using a plan→apply split. +- Production deploy workflow (`.github/workflows/deploy.yml`) triggers on completion of either Main CI workflow and applies Terraform in `terraform/` using a plan→apply split; `apply` can be skipped if there are no Terraform changes. - Infra wiring (secrets, services, ingress, image vars) is defined in `terraform/main.tf`; an alternative GCP target is under `terraform-gcp/`. ## Project conventions to preserve diff --git a/CHANGELOG.md b/CHANGELOG.md index c88a0ce..e0f7506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed -- Removed deployments to staging in PR pipelines. PR only runs CI now. +- Removed deployments to staging in PR pipelines. PR only runs CI now. Closes [#14](https://lightroasted.vps-kinghost.net/rmcampos/tasknote/issues/14) ### Removed - Old files from project and moved scripts to `tools` folder. diff --git a/Taskfile.yml b/Taskfile.yml index c65e8dd..ca9f225 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -2,9 +2,6 @@ version: '3' -vars: - GREETING: Hello, World! - tasks: docker-build-web: desc: Build the tasknote-web prod-ready docker image, tagging it as candidate diff --git a/doppler.yaml b/doppler.yaml new file mode 100644 index 0000000..737cf63 --- /dev/null +++ b/doppler.yaml @@ -0,0 +1,3 @@ +setup: + project: tasknote + config: prd diff --git a/terraform-gcp/.terraform.lock.hcl b/terraform-gcp/.terraform.lock.hcl deleted file mode 100644 index 576e316..0000000 --- a/terraform-gcp/.terraform.lock.hcl +++ /dev/null @@ -1,22 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/google" { - version = "5.45.2" - constraints = "~> 5.0" - hashes = [ - "h1:k8taQAdfHrv2F/AiGV5BZBZfI+1uaq8g6O8dWzjx42c=", - "zh:0d09c8f20b556305192cdbe0efa6d333ceebba963a8ba91f9f1714b5a20c4b7a", - "zh:117143fc91be407874568df416b938a6896f94cb873f26bba279cedab646a804", - "zh:16ccf77d18dd2c5ef9c0625f9cf546ebdf3213c0a452f432204c69feed55081e", - "zh:3e555cf22a570a4bd247964671f421ed7517970cd9765ceb46f335edc2c6f392", - "zh:688bd5b05a75124da7ae6e885b2b92bd29f4261808b2b78bd5f51f525c1052ca", - "zh:6db3ef37a05010d82900bfffb3261c59a0c247e0692049cb3eb8c2ef16c9d7bf", - "zh:70316fde75f6a15d72749f66d994ccbdde5f5ed4311b6d06b99850f698c9bbf9", - "zh:84b8e583771a4f2bd514e519d98ed7fd28dce5efe0634e973170e1cfb5556fb4", - "zh:9d4b8ef0a9b6677935c604d94495042e68ff5489932cfd1ec41052e094a279d3", - "zh:a2089dd9bd825c107b148dd12d6b286f71aa37dfd4ca9c35157f2dcba7bc19d8", - "zh:f03d795c0fd9721e59839255ee7ba7414173017dc530b4ce566daf3802a0d6dd", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - ] -} diff --git a/terraform-gcp/cloudrun.tf b/terraform-gcp/cloudrun.tf deleted file mode 100644 index e4523ad..0000000 --- a/terraform-gcp/cloudrun.tf +++ /dev/null @@ -1,127 +0,0 @@ -resource "google_cloud_run_v2_service" "backend" { - name = "tasknote-api" - location = var.region - ingress = "INGRESS_TRAFFIC_ALL" - - depends_on = [google_project_service.run] - - template { - service_account = google_service_account.cloudrun_sa.email - vpc_access { - connector = google_vpc_access_connector.connector.id - egress = "PRIVATE_RANGES_ONLY" - } - - containers { - image = var.backend_image - ports { - container_port = 8585 - } - env { - name = "POSTGRES_DB" - value = var.db_name - } - env { - name = "POSTGRES_HOST" - value = google_sql_database_instance.instance.private_ip_address - } - env { - name = "POSTGRES_USER" - value_source { - secret_key_ref { - secret = google_secret_manager_secret.db_user.secret_id - version = google_secret_manager_secret_version.db_user_version.version - } - } - } - env { - name = "POSTGRES_PASSWORD" - value_source { - secret_key_ref { - secret = google_secret_manager_secret.db_password.secret_id - version = google_secret_manager_secret_version.db_password_version.version - } - } - } - env { - name = "POSTGRES_PORT" - value = "5432" - } - env { - name = "CORS_ALLOWED_ORIGINS" - value = var.cors_allowed_origins - } - env { - name = "SERVER_SERVLET_CONTEXT_PATH" - value = "/" - } - env { - name = "TARGET_ENV" - value = "production" - } - env { - name = "SECURITY_KEY" - value_source { - secret_key_ref { - secret = google_secret_manager_secret.security_key.secret_id - version = google_secret_manager_secret_version.security_key_version.version - } - } - } - env { - name = "MAILGUN_APIKEY" - value_source { - secret_key_ref { - secret = google_secret_manager_secret.mailgun_apikey.secret_id - version = google_secret_manager_secret_version.mailgun_apikey_version.version - } - } - } - } - } -} - -resource "google_cloud_run_v2_service" "frontend" { - name = "tasknote-app" - location = var.region - ingress = "INGRESS_TRAFFIC_ALL" - - depends_on = [google_project_service.run] - - template { - service_account = google_service_account.cloudrun_sa.email - - containers { - image = var.frontend_image - ports { - container_port = 5000 - } - env { - name = "VITE_BACKEND_SERVER" - value = google_cloud_run_v2_service.backend.uri - } - } - } -} - -# Allow unauthenticated access to both services -resource "google_cloud_run_service_iam_member" "backend_public" { - location = google_cloud_run_v2_service.backend.location - service = google_cloud_run_v2_service.backend.name - role = "roles/run.invoker" - member = "allUsers" -} - -resource "google_cloud_run_service_iam_member" "frontend_public" { - location = google_cloud_run_v2_service.frontend.location - service = google_cloud_run_v2_service.frontend.name - role = "roles/run.invoker" - member = "allUsers" -} - -# Grant Cloud SQL Client role to the service account -resource "google_project_iam_member" "cloudsql_client" { - project = var.project_id - role = "roles/cloudsql.client" - member = "serviceAccount:${google_service_account.cloudrun_sa.email}" -} diff --git a/terraform-gcp/database.tf b/terraform-gcp/database.tf deleted file mode 100644 index d7a102d..0000000 --- a/terraform-gcp/database.tf +++ /dev/null @@ -1,32 +0,0 @@ -resource "google_sql_database_instance" "instance" { - name = "tasknote-db-instance" - region = var.region - database_version = "POSTGRES_15" - - depends_on = [ - google_service_networking_connection.private_vpc_connection, - google_project_service.sqladmin, - google_project_service.servicenetworking - ] - - settings { - tier = "db-f1-micro" # Smallest tier for dev/small app - ip_configuration { - ipv4_enabled = false - private_network = google_compute_network.vpc_network.id - } - } - - deletion_protection = false # Set to true for production -} - -resource "google_sql_database" "database" { - name = var.db_name - instance = google_sql_database_instance.instance.name -} - -resource "google_sql_user" "users" { - name = var.db_user - instance = google_sql_database_instance.instance.name - password = var.db_password -} diff --git a/terraform-gcp/docker-registry-push.sh b/terraform-gcp/docker-registry-push.sh deleted file mode 100755 index 567ed71..0000000 --- a/terraform-gcp/docker-registry-push.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -PROJECT_ID="replace-me" -VERSION="23" -SOURCE_IMG="ghcr.io/rmcampos/tasknote/api:$VERSION" -DESTINATION_IMG="us-central1-docker.pkg.dev/$PROJECT_ID/tasknote-repo/api:$VERSION" -GHCR_PAT="replace-me" - -# Login -echo $GHCR_PAT | docker login ghcr.io -u user --password-stdin -echo "Logged in on ghcr.io" - -gcloud auth configure-docker us-central1-docker.pkg.dev -docker login us-central1-docker.pkg.dev -echo "Logged in on GCP Container Registry" - -crane auth login ghcr.io -u RMCampos -p $GHCR_PAT -echo "Logged crane in on ghcr.io" - -# Check if image already exists in destination -echo "Checking if $DESTINATION_IMG already exists in destination registry..." -if crane manifest "$DESTINATION_IMG" > /dev/null 2>&1; then - echo "Image $DESTINATION_IMG already exists. Skipping push." -else - echo "Image not found in destination. Proceeding with push..." - - docker pull $SOURCE_IMG - docker tag $SOURCE_IMG $DESTINATION_IMG - docker push $DESTINATION_IMG - echo "Pushed image to GCP Container Registry" - - crane copy $SOURCE_IMG $DESTINATION_IMG - echo "Copied image to GCP Container Registry with crane" -fi - -# Verification -echo "Verifying push..." -if crane manifest "$DESTINATION_IMG" > /dev/null 2>&1; then - echo "SUCCESS: Image $DESTINATION_IMG is properly pushed and available in the registry." - gcloud artifacts docker images list us-central1-docker.pkg.dev/project-9f22294e-b6f5-41de-83c/tasknote-repo | grep "api:$VERSION" -else - echo "FAILURE: Image $DESTINATION_IMG was not found in the destination registry." - exit 1 -fi - diff --git a/terraform-gcp/network.tf b/terraform-gcp/network.tf deleted file mode 100644 index 5bd899c..0000000 --- a/terraform-gcp/network.tf +++ /dev/null @@ -1,35 +0,0 @@ -resource "google_compute_network" "vpc_network" { - name = "tasknote-vpc" - auto_create_subnetworks = false -} - -resource "google_compute_subnetwork" "subnet" { - name = "tasknote-subnet" - ip_cidr_range = "10.0.0.0/24" - region = var.region - network = google_compute_network.vpc_network.id -} - -# Private IP for Cloud SQL -resource "google_compute_global_address" "private_ip_address" { - name = "tasknote-private-ip" - purpose = "VPC_PEERING" - address_type = "INTERNAL" - prefix_length = 16 - network = google_compute_network.vpc_network.id -} - -resource "google_service_networking_connection" "private_vpc_connection" { - network = google_compute_network.vpc_network.id - service = "servicenetworking.googleapis.com" - reserved_peering_ranges = [google_compute_global_address.private_ip_address.name] -} - -# Serverless VPC Access Connector -resource "google_vpc_access_connector" "connector" { - name = "tasknote-vpc-connector" - region = var.region - ip_cidr_range = "10.8.0.0/28" - network = google_compute_network.vpc_network.name - depends_on = [google_project_service.vpcaccess, google_project_service.compute] -} diff --git a/terraform-gcp/outputs.tf b/terraform-gcp/outputs.tf deleted file mode 100644 index 3ef3225..0000000 --- a/terraform-gcp/outputs.tf +++ /dev/null @@ -1,15 +0,0 @@ -output "backend_url" { - value = google_cloud_run_v2_service.backend.uri -} - -output "frontend_url" { - value = google_cloud_run_v2_service.frontend.uri -} - -output "cloud_sql_instance_ip" { - value = google_sql_database_instance.instance.private_ip_address -} - -output "artifact_registry_repo" { - value = google_artifact_registry_repository.repo.name -} diff --git a/terraform-gcp/providers.tf b/terraform-gcp/providers.tf deleted file mode 100644 index ae5972a..0000000 --- a/terraform-gcp/providers.tf +++ /dev/null @@ -1,25 +0,0 @@ -terraform { - required_providers { - google = { - source = "hashicorp/google" - version = "~> 5.0" - } - } - - backend "s3" { - bucket = "tasknote" - key = "gcp/terraform.tfstate" - region = "auto" - endpoints = { s3 = "https://d17eb09b6bce2f90e16e800bb2a6baf9.r2.cloudflarestorage.com" } - skip_credentials_validation = true - skip_region_validation = true - skip_requesting_account_id = true - skip_metadata_api_check = true - skip_s3_checksum = true - } -} - -provider "google" { - project = var.project_id - region = var.region -} diff --git a/terraform-gcp/registry.tf b/terraform-gcp/registry.tf deleted file mode 100644 index e13a415..0000000 --- a/terraform-gcp/registry.tf +++ /dev/null @@ -1,7 +0,0 @@ -resource "google_artifact_registry_repository" "repo" { - location = var.region - repository_id = "tasknote-repo" - description = "Docker repository for TaskNote images" - format = "DOCKER" - depends_on = [google_project_service.artifactregistry] -} diff --git a/terraform-gcp/secrets.tf b/terraform-gcp/secrets.tf deleted file mode 100644 index 171075f..0000000 --- a/terraform-gcp/secrets.tf +++ /dev/null @@ -1,95 +0,0 @@ -resource "google_service_account" "cloudrun_sa" { - account_id = "tasknote-cloudrun-sa" - display_name = "TaskNote Cloud Run Service Account" -} - -resource "google_secret_manager_secret" "db_password" { - secret_id = "db-password" - replication { - user_managed { - replicas { - location = var.region - } - } - } - depends_on = [google_project_service.secretmanager] -} - -resource "google_secret_manager_secret_version" "db_password_version" { - secret = google_secret_manager_secret.db_password.id - secret_data = var.db_password -} - -resource "google_secret_manager_secret" "db_user" { - secret_id = "db-user" - replication { - user_managed { - replicas { - location = var.region - } - } - } - depends_on = [google_project_service.secretmanager] -} - -resource "google_secret_manager_secret_version" "db_user_version" { - secret = google_secret_manager_secret.db_user.id - secret_data = var.db_user -} - -resource "google_secret_manager_secret_iam_member" "db_password_access" { - secret_id = google_secret_manager_secret.db_password.id - role = "roles/secretmanager.secretAccessor" - member = "serviceAccount:${google_service_account.cloudrun_sa.email}" -} - -resource "google_secret_manager_secret_iam_member" "db_user_access" { - secret_id = google_secret_manager_secret.db_user.id - role = "roles/secretmanager.secretAccessor" - member = "serviceAccount:${google_service_account.cloudrun_sa.email}" -} - -resource "google_secret_manager_secret" "security_key" { - secret_id = "security-key" - replication { - user_managed { - replicas { - location = var.region - } - } - } -} - -resource "google_secret_manager_secret_version" "security_key_version" { - secret = google_secret_manager_secret.security_key.id - secret_data = var.security_key -} - -resource "google_secret_manager_secret" "mailgun_apikey" { - secret_id = "mailgun-apikey" - replication { - user_managed { - replicas { - location = var.region - } - } - } - depends_on = [google_project_service.secretmanager] -} - -resource "google_secret_manager_secret_version" "mailgun_apikey_version" { - secret = google_secret_manager_secret.mailgun_apikey.id - secret_data = var.mailgun_apikey -} - -resource "google_secret_manager_secret_iam_member" "security_key_access" { - secret_id = google_secret_manager_secret.security_key.id - role = "roles/secretmanager.secretAccessor" - member = "serviceAccount:${google_service_account.cloudrun_sa.email}" -} - -resource "google_secret_manager_secret_iam_member" "mailgun_apikey_access" { - secret_id = google_secret_manager_secret.mailgun_apikey.id - role = "roles/secretmanager.secretAccessor" - member = "serviceAccount:${google_service_account.cloudrun_sa.email}" -} diff --git a/terraform-gcp/services.tf b/terraform-gcp/services.tf deleted file mode 100644 index 8640268..0000000 --- a/terraform-gcp/services.tf +++ /dev/null @@ -1,34 +0,0 @@ -resource "google_project_service" "compute" { - service = "compute.googleapis.com" - disable_on_destroy = false -} - -resource "google_project_service" "sqladmin" { - service = "sqladmin.googleapis.com" - disable_on_destroy = false -} - -resource "google_project_service" "run" { - service = "run.googleapis.com" - disable_on_destroy = false -} - -resource "google_project_service" "secretmanager" { - service = "secretmanager.googleapis.com" - disable_on_destroy = false -} - -resource "google_project_service" "vpcaccess" { - service = "vpcaccess.googleapis.com" - disable_on_destroy = false -} - -resource "google_project_service" "artifactregistry" { - service = "artifactregistry.googleapis.com" - disable_on_destroy = false -} - -resource "google_project_service" "servicenetworking" { - service = "servicenetworking.googleapis.com" - disable_on_destroy = false -} diff --git a/terraform-gcp/terraform.tfvars.example b/terraform-gcp/terraform.tfvars.example deleted file mode 100644 index aa50aaa..0000000 --- a/terraform-gcp/terraform.tfvars.example +++ /dev/null @@ -1,10 +0,0 @@ -project_id = "your-gcp-project-id" -region = "us-central1" -db_user = "tasknoteuser" -db_password = "your-db-password" -db_name = "tasknote" -security_key = "your-security-key" -mailgun_apikey = "your-mailgun-apikey" -backend_image = "ghcr.io/rmcampos/tasknote/api:latest" -frontend_image = "ghcr.io/rmcampos/tasknote/app:latest" -cors_allowed_origins = "*" diff --git a/terraform-gcp/variables.tf b/terraform-gcp/variables.tf deleted file mode 100644 index bd89ea2..0000000 --- a/terraform-gcp/variables.tf +++ /dev/null @@ -1,50 +0,0 @@ -variable "project_id" { - type = string - description = "The GCP Project ID" -} - -variable "region" { - type = string - default = "us-central1" - description = "The GCP region to deploy resources" -} - -variable "db_user" { - type = string - sensitive = true -} - -variable "db_password" { - type = string - sensitive = true -} - -variable "db_name" { - type = string - default = "tasknote" -} - -variable "security_key" { - type = string - sensitive = true -} - -variable "mailgun_apikey" { - type = string - sensitive = true -} - -variable "backend_image" { - type = string - default = "ghcr.io/rmcampos/tasknote/api:latest" -} - -variable "frontend_image" { - type = string - default = "ghcr.io/rmcampos/tasknote/app:latest" -} - -variable "cors_allowed_origins" { - type = string - default = "*" -} diff --git a/terraform-stg/.terraform.lock.hcl b/terraform-stg/.terraform.lock.hcl deleted file mode 100644 index 728beb3..0000000 --- a/terraform-stg/.terraform.lock.hcl +++ /dev/null @@ -1,22 +0,0 @@ -# This file is maintained automatically by "terraform init". -# Manual edits may be lost in future updates. - -provider "registry.terraform.io/hashicorp/kubernetes" { - version = "2.38.0" - constraints = "2.38.0" - hashes = [ - "h1:5CkveFo5ynsLdzKk+Kv+r7+U9rMrNjfZPT3a0N/fhgE=", - "zh:0af928d776eb269b192dc0ea0f8a3f0f5ec117224cd644bdacdc682300f84ba0", - "zh:1be998e67206f7cfc4ffe77c01a09ac91ce725de0abaec9030b22c0a832af44f", - "zh:326803fe5946023687d603f6f1bab24de7af3d426b01d20e51d4e6fbe4e7ec1b", - "zh:4a99ec8d91193af961de1abb1f824be73df07489301d62e6141a656b3ebfff12", - "zh:5136e51765d6a0b9e4dbcc3b38821e9736bd2136cf15e9aac11668f22db117d2", - "zh:63fab47349852d7802fb032e4f2b6a101ee1ce34b62557a9ad0f0f0f5b6ecfdc", - "zh:924fb0257e2d03e03e2bfe9c7b99aa73c195b1f19412ca09960001bee3c50d15", - "zh:b63a0be5e233f8f6727c56bed3b61eb9456ca7a8bb29539fba0837f1badf1396", - "zh:d39861aa21077f1bc899bc53e7233262e530ba8a3a2d737449b100daeb303e4d", - "zh:de0805e10ebe4c83ce3b728a67f6b0f9d18be32b25146aa89116634df5145ad4", - "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", - "zh:faf23e45f0090eef8ba28a8aac7ec5d4fdf11a36c40a8d286304567d71c1e7db", - ] -} diff --git a/terraform-stg/main.tf b/terraform-stg/main.tf deleted file mode 100644 index 1a4e316..0000000 --- a/terraform-stg/main.tf +++ /dev/null @@ -1,387 +0,0 @@ -terraform { - required_providers { - kubernetes = { - source = "hashicorp/kubernetes" - version = "= 2.38.0" - } - } - - backend "s3" { - bucket = "tasknote-stg" - key = "kubernetes/terraform.tfstate" - region = "auto" - endpoints = { s3 = "https://d17eb09b6bce2f90e16e800bb2a6baf9.r2.cloudflarestorage.com" } - skip_credentials_validation = true - skip_region_validation = true - skip_requesting_account_id = true - skip_metadata_api_check = true - skip_s3_checksum = true - } -} - -provider "kubernetes" { - config_path = "~/.kube/config" -} - -variable "db_user" { - type = string - sensitive = true -} - -variable "db_password" { - type = string - sensitive = true -} - -variable "db_name" { - type = string - sensitive = true -} - -variable "security_key" { - type = string - sensitive = true -} - -variable "mailgun_apikey" { - type = string - sensitive = true -} - -variable "cors_allowed_origins" { - type = string - default = "https://tasknote-stg.darkroasted.vps-kinghost.net" -} - -variable "root_log_level" { - type = string - default = "INFO" -} - -variable "backend_image" { - type = string - default = "ghcr.io/rmcampos/tasknote/api:candidate" -} - -variable "frontend_image" { - type = string - default = "ghcr.io/rmcampos/tasknote/app:candidate" -} - -variable "deploy_version" { - type = string - default = "manual" -} - -resource "kubernetes_namespace_v1" "tasknote_stg" { - metadata { - name = "tasknote-stg" - } -} - -resource "kubernetes_secret_v1" "tasknote_stg_secrets" { - metadata { - name = "tasknote-stg-secrets" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - - data = { - postgres_user = var.db_user - postgres_password = var.db_password - postgres_db = var.db_name - security_key = var.security_key - mailgun_apikey = var.mailgun_apikey - } -} - -resource "kubernetes_persistent_volume_claim_v1" "tasknote_stg_db_data" { - metadata { - name = "postgres-data-pvc" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - spec { - access_modes = ["ReadWriteOnce"] - resources { - requests = { - storage = "1Gi" - } - } - } -} - -resource "kubernetes_deployment_v1" "tasknote_stg_db" { - metadata { - name = "tasknote-stg-db" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - spec { - replicas = 1 - selector { match_labels = { app = "tasknote-stg-db" } } - template { - metadata { labels = { app = "tasknote-stg-db" } } - spec { - container { - image = "postgres:15.8-bookworm" - name = "postgres" - volume_mount { - name = "postgres-storage" - mount_path = "/var/lib/postgresql/data" - } - env { - name = "POSTGRES_USER" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "postgres_user" - } - } - } - env { - name = "POSTGRES_PASSWORD" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "postgres_password" - } - } - } - env { - name = "POSTGRES_DB" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "postgres_db" - } - } - } - port { container_port = 5432 } - } - volume { - name = "postgres-storage" - persistent_volume_claim { - claim_name = kubernetes_persistent_volume_claim_v1.tasknote_stg_db_data.metadata[0].name - } - } - } - } - } -} - -resource "kubernetes_service_v1" "tasknote_stg_db_svc" { - metadata { - name = "tasknote-stg-db-svc" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - spec { - selector = { app = "tasknote-stg-db" } - port { port = 5432 } - type = "ClusterIP" - } -} - -resource "kubernetes_deployment_v1" "tasknote_stg_backend" { - metadata { - name = "tasknote-stg-backend" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - spec { - replicas = 1 - selector { match_labels = { app = "tasknote-stg-backend" } } - template { - metadata { - labels = { app = "tasknote-stg-backend" } - annotations = { - "deploy_id" = var.deploy_version - } - } - spec { - container { - image = var.backend_image - name = "backend" - image_pull_policy = "Always" - env { - name = "POSTGRES_DB" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "postgres_db" - } - } - } - env { - name = "POSTGRES_HOST" - value = "tasknote-stg-db-svc" - } - env { - name = "POSTGRES_USER" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "postgres_user" - } - } - } - env { - name = "POSTGRES_PASSWORD" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "postgres_password" - } - } - } - env { - name = "POSTGRES_PORT" - value = "5432" - } - env { - name = "CORS_ALLOWED_ORIGINS" - value = var.cors_allowed_origins - } - env { - name = "SERVER_SERVLET_CONTEXT_PATH" - value = "/" - } - env { - name = "ROOT_LOG_LEVEL" - value = var.root_log_level - } - env { - name = "SECURITY_KEY" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "security_key" - } - } - } - env { - name = "TARGET_ENV" - value = "staging" - } - env { - name = "MAILGUN_APIKEY" - value_from { - secret_key_ref { - name = kubernetes_secret_v1.tasknote_stg_secrets.metadata[0].name - key = "mailgun_apikey" - } - } - } - resources { - limits = { memory = "256Mi", cpu = "500m" } - requests = { memory = "256Mi", cpu = "250m" } - } - } - } - } - } -} - -resource "kubernetes_service_v1" "tasknote_stg_backend_svc" { - metadata { - name = "tasknote-stg-backend-svc" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - spec { - selector = { app = "tasknote-stg-backend" } - port { - port = 8585 - target_port = 8585 - } - } -} - -resource "kubernetes_deployment_v1" "tasknote_stg_frontend" { - metadata { - name = "tasknote-stg-frontend" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - spec { - replicas = 1 - selector { match_labels = { app = "tasknote-stg-app" } } - template { - metadata { - labels = { app = "tasknote-stg-app" } - annotations = { - "deploy_id" = var.deploy_version - } - } - spec { - container { - image = var.frontend_image - name = "frontend" - image_pull_policy = "Always" - port { container_port = 5000 } - env { - name = "VITE_BACKEND_SERVER" - value = "https://tasknoteapi-stg.darkroasted.vps-kinghost.net" - } - } - } - } - } -} - -resource "kubernetes_service_v1" "tasknote_stg_frontend_svc" { - metadata { - name = "tasknote-stg-frontend-svc" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - } - spec { - selector = { app = "tasknote-stg-app" } - port { - port = 5000 - target_port = 5000 - } - type = "ClusterIP" - } -} - -# Unified Ingress for App and API -resource "kubernetes_ingress_v1" "tasknote_stg_ingress" { - metadata { - name = "tasknote-stg-ingress" - namespace = kubernetes_namespace_v1.tasknote_stg.metadata[0].name - annotations = { - "kubernetes.io/ingress.class" = "traefik" - "cert-manager.io/cluster-issuer" = "letsencrypt-prod" - } - } - spec { - tls { - hosts = ["tasknote-stg.darkroasted.vps-kinghost.net", "tasknoteapi-stg.darkroasted.vps-kinghost.net"] - secret_name = "tasknote-stg-tls-certs" - } - rule { - host = "tasknote-stg.darkroasted.vps-kinghost.net" - http { - path { - path = "/" - path_type = "Prefix" - backend { - service { - name = kubernetes_service_v1.tasknote_stg_frontend_svc.metadata[0].name - port { number = 5000 } - } - } - } - } - } - rule { - host = "tasknoteapi-stg.darkroasted.vps-kinghost.net" - http { - path { - path = "/" - path_type = "Prefix" - backend { - service { - name = kubernetes_service_v1.tasknote_stg_backend_svc.metadata[0].name - port { number = 8585 } - } - } - } - } - } - } -}