Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/workflows/deploy-api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Deploy API

on:
push:
branches: [main, test]
paths:
- 'backend/api/**'
- 'backend/shared/**'
- 'backend/email/**'
- 'common/**'
- '.github/workflows/deploy-api.yml'

env:
REGION: us-west1
ZONE: us-west1-b
PROJECT_ID: polylove
SERVICE_NAME: api

jobs:
deploy:
name: Deploy API
runs-on: ubuntu-latest
environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'development' }}

steps:
- uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '19'
cache: 'yarn'

# This caches node_modules based on yarn.lock hash
- name: Cache node_modules
uses: actions/cache@v3
with:
path: |
**/node_modules
~/.cache/yarn
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-modules-

- name: Install dependencies
working-directory: backend/api
run: yarn install --frozen-lockfile

- name: Build API
working-directory: backend/api
run: yarn build

- name: Set up Cloud SDK
uses: google-github-actions/setup-gcloud@v1
with:
project_id: ${{ env.PROJECT_ID }}
service_account_key: ${{ secrets.GCP_SA_KEY }}
export_default_credentials: true

- name: Configure Docker for GCP
run: |
gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev

- name: Set deployment variables
run: |
echo "IMAGE_TAG=$(date +%s)-${GITHUB_SHA::7}" >> $GITHUB_ENV
echo "IMAGE_URL=${{ env.REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/builds/${{ env.SERVICE_NAME }}:${IMAGE_TAG}" >> $GITHUB_ENV

- name: Build and push Docker image
working-directory: backend/api
run: |
docker build . --tag ${{ env.IMAGE_URL }} --platform linux/amd64
docker push ${{ env.IMAGE_URL }}

- name: Install OpenTofu
run: |
wget https://github.com/opentofu/opentofu/releases/download/v1.6.0/tofu_1.6.0_linux_amd64.zip
unzip tofu_1.6.0_linux_amd64.zip
sudo mv tofu /usr/local/bin/
tofu --version

- name: Deploy with OpenTofu
working-directory: backend/api
env:
TF_VAR_image_url: ${{ env.IMAGE_URL }}
TF_VAR_env: ${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
run: |
tofu init
tofu apply -auto-approve
54 changes: 53 additions & 1 deletion backend/api/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,57 @@ provider "google" {
zone = local.zone
}

# Service account for the API instances
resource "google_service_account" "api_service_account" {
account_id = "${local.service_name}-sa"
display_name = "Service Account for ${local.service_name}"
description = "Used by the API service instances"
}

# Grant minimal required permissions
resource "google_project_iam_member" "api_sa_roles" {
for_each = toset([
"roles/secretmanager.secretAccessor", # To access secrets
"roles/monitoring.metricWriter", # To write metrics
"roles/logging.logWriter", # To write logs
"roles/storage.objectViewer", # To read from storage
])

project = local.project
role = each.key
member = "serviceAccount:${google_service_account.api_service_account.email}"
}

# Service account for GitHub Actions
resource "google_service_account" "github_actions" {
account_id = "github-actions"
display_name = "GitHub Actions"
description = "Used by GitHub Actions for CI/CD"
}

# Grant required permissions to GitHub Actions SA
resource "google_project_iam_member" "github_actions_roles" {
for_each = toset([
"roles/compute.admin", # To manage compute resources
"roles/artifactregistry.writer", # To push Docker images
])

project = local.project
role = each.key
member = "serviceAccount:${google_service_account.github_actions.email}"
}

# Create a service account key for GitHub Actions
resource "google_service_account_key" "github_actions_key" {
service_account_id = google_service_account.github_actions.name
}

# Output the key (this will be sensitive)
output "github_actions_key" {
value = google_service_account_key.github_actions_key.private_key
sensitive = true
}

# Firebase Storage Buckets
# Note you still have to deploy the rules: `firebase deploy --only storage`
resource "google_storage_bucket" "public_storage" {
Expand Down Expand Up @@ -86,7 +137,8 @@ resource "google_compute_instance_template" "api_template" {
}

service_account {
scopes = ["cloud-platform"]
email = google_service_account.api_service_account.email
scopes = ["cloud-platform"] # We can be more restrictive here if needed
}

metadata = {
Expand Down
11 changes: 11 additions & 0 deletions setup_api_ci_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
1. First apply the OpenTofu changes to create the service accounts:
```bash
cd backend/api
tofu init
tofu apply
```
2. Get the GitHub Actions key:
```bash
tofu output -json github_actions_key | jq -r
```
3. Add this key as a GitHub secret named `GCP_SA_KEY` in your repository settings