Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.
Merged
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
108 changes: 108 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# API_Gateway/.github/workflows/build.yml
# This workflow builds, tests, AND packages the Go API Gateway

name: Build, Test, and Package Gateway

on:
push:
branches:
- 'main'
- 'devOps'
- 'dev'
pull_request:
branches:
- 'main'
- 'devOps'
- 'dev'

permissions:
contents: read
packages: write

jobs:
# --- JOB 1: Build and Test (From your file) ---
# This runs on all pushes AND all pull requests to verify the code
build-and-test:
name: Build and Smoke Test
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
cache-dependency-path: go.mod

- name: Download dependencies
run: go mod download

- name: Build Go Application
run: go build -v -o ./app ./cmd/gateway

- name: Verify config.yaml exists
run: |
if [ ! -f config.yaml ]; then
echo "Error: config.yaml not found!"
exit 1
fi
echo "✓ config.yaml found"

- name: Start Gateway (smoke test)
run: |
echo "Starting API Gateway with config.yaml..."
# We need to set a dummy secret for the smoke test
export JWT_SECRET="dummy-secret-for-test"
timeout 5s ./app || code=$?
if [ ${code:-0} -eq 124 ]; then
echo "✓ Gateway started successfully (terminated after 5s)"
exit 0
elif [ ${code:-0} -eq 0 ]; then
echo "✓ Gateway started and stopped cleanly"
exit 0
else
echo "✗ Gateway failed to start (exit code: ${code})"
exit 1
fi

# --- JOB 2: Package as Docker Image (From my file) ---
# This runs ONLY after Job 1 succeeds, AND
# This runs ONLY on pushes to your main branches (not PRs)
build-and-push-docker:
name: Build & Push Docker Image
needs: build-and-test # Depends on the first job

# This logic ensures it only runs on pushes, not PRs
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/devOps' || github.ref == 'refs/heads/dev')
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha,prefix=
type:raw,value=latest,enable={{is_default_branch}}

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
60 changes: 0 additions & 60 deletions .github/workflows/buildtest.yaml

This file was deleted.

63 changes: 63 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# API_Gateway/.github/workflows/deploy.yml

name: Deploy Gateway to Kubernetes

on:
workflow_run:
# This MUST match the 'name:' of your build.yml file
workflows: ["Build, Test, and Package Gateway"]
types:
- completed # Run only after the build workflow finishes
branches:
- 'main' # Deploy on main
- 'devOps' # Deploy on devOps
# Note: We are not deploying the 'dev' branch, as requested

jobs:
deploy:
name: Deploy Gateway to Kubernetes
# We only deploy if the build job was successful
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest

steps:
# Checks out the code so we can access the K8s files
- name: Checkout Code
uses: actions/checkout@v4

# Finds the 7-character commit SHA that triggered the build
- name: Get Commit SHA
id: get_sha
run: |
echo "sha=$(echo ${{ github.event.workflow_run.head_sha }} | cut -c1-7)" >> $GITHUB_OUTPUT

# Installs kubectl
- name: Install kubectl
uses: azure/setup-kubectl@v3

# Installs yq, the tool we use to safely edit YAML
- name: Install yq
run: |
sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq
sudo chmod +x /usr/bin/yq

# Logs into your Azure VM's K3s cluster
- name: Set Kubernetes context
uses: azure/k8s-set-context@v4
with:
# This uses the Org-level secret you created
kubeconfig: ${{ secrets.KUBE_CONFIG_DATA }}

# This is the magic step!
# It replaces the image tag in your K8s file with the new one
- name: Update image tag in YAML
run: |
yq -i '.spec.template.spec.containers[0].image = "ghcr.io/techtorque-2025/api_gateway:${{ steps.get_sha.outputs.sha }}"' Kubernetes/services/gateway-deployment.yaml

# Applies the updated file to your cluster
- name: Deploy to Kubernetes
run: |
kubectl apply -f Kubernetes/services/gateway-deployment.yaml

# Waits for the new version to be fully rolled out
kubectl rollout status deployment/api-gateway-deployment
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@ Thumbs.db
# JetBrains
workspace.xml

# Build Artefacts
bin/

# Keep go.mod and source files tracked (do not ignore go.mod/go.sum)
43 changes: 22 additions & 21 deletions cmd/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type ServiceConfig struct {
TargetURL string `yaml:"target_url"`
StripPrefix string `yaml:"strip_prefix"`
AuthRequired bool `yaml:"auth_required"`
EnvVar string `yaml:"env_var"` // Optional: custom environment variable name
}

// --- Global Logger ---
Expand All @@ -59,28 +60,28 @@ func loadConfig(path string) (*Config, error) {
config.JWTSecret = secret
}

// It maps the 'name' from config.yaml to the environment variable name
// from docker-compose.yml.
serviceURLEnvMap := map[string]string{
"auth": "AUTH_SERVICE_URL",
"users": "AUTH_SERVICE_URL", // "users" also points to the auth service
"vehicles": "VEHICLE_SERVICE_URL",
"appointments": "APPOINTMENTS_SERVICE_URL",
"services": "PROJECT_SERVICE_URL", // As defined in docker-compose, this is the project service
"projects": "PROJECT_SERVICE_URL", // "projects" also points to this service
"time-logs": "TIME_LOGGING_SERVICE_URL",
"payments": "PAYMENT_SERVICE_URL",
"invoices": "PAYMENT_SERVICE_URL", // "invoices" also points to this service
"admin": "ADMIN_SERVICE_URL",
"websocket": "WS_SERVICE_URL",
"ai": "AI_SERVICE_URL",
}

// Override service URLs from environment variables
// If env_var is specified in config, use it; otherwise generate from service name
for i := range config.Services {
if envVar, ok := serviceURLEnvMap[config.Services[i].Name]; ok {
if url := os.Getenv(envVar); url != "" {
config.Services[i].TargetURL = url
}
var envVarName string

// Use custom env_var if specified, otherwise auto-generate
if config.Services[i].EnvVar != "" {
envVarName = config.Services[i].EnvVar
} else {
// Auto-generate: convert "service-name" to "SERVICE_NAME_SERVICE_URL"
// e.g., "time-logs" -> "TIME_LOGS_SERVICE_URL"
normalizedName := strings.ToUpper(strings.ReplaceAll(config.Services[i].Name, "-", "_"))
envVarName = normalizedName + "_SERVICE_URL"
}

// Override target URL if environment variable is set
if envURL := os.Getenv(envVarName); envURL != "" {
config.Services[i].TargetURL = envURL
logger.Info("Service URL overridden from environment",
"service", config.Services[i].Name,
"env_var", envVarName,
"url", envURL)
}
}

Expand Down
Loading