Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.
Merged

Dev #16

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
34 changes: 34 additions & 0 deletions .github/workflows/build-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Build and Test

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

permissions:
contents: read

jobs:
build-test:
name: Build and Test
runs-on: ubuntu-latest

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

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
cache: true

- name: Build
run: go build -v ./...

- name: Test Summary
run: |
echo "### ✅ Build Successful" >> $GITHUB_STEP_SUMMARY
echo "Go build completed successfully" >> $GITHUB_STEP_SUMMARY
echo "Ready for review and merge" >> $GITHUB_STEP_SUMMARY
96 changes: 25 additions & 71 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -1,95 +1,41 @@
# API_Gateway/.github/workflows/build.yml
# This workflow builds, tests, AND packages the Go API Gateway

name: Build, Test, and Package Gateway
name: Build and Push Docker Image

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

permissions:
contents: read
packages: write
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
build-and-push:
name: Build & Push Docker Image
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
- name: Extract branch name
id: branch
run: |
if [ ! -f config.yaml ]; then
echo "Error: config.yaml not found!"
exit 1
fi
echo "✓ config.yaml found"
BRANCH_NAME=${GITHUB_REF#refs/heads/}
echo "name=${BRANCH_NAME}" >> $GITHUB_OUTPUT
echo "📍 Building for branch: ${BRANCH_NAME}"

- 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
- name: Docker meta (with branch-aware tags)
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
images: ghcr.io/techtorque-2025/api_gateway
tags: |
type=sha,prefix=
type:raw,value=latest,enable={{is_default_branch}}
type=raw,value=${{ steps.branch.outputs.name }}-{{sha}},enable=true
type=raw,value=latest,enable={{is_default_branch}}
flavor: |
latest=false

- name: Log in to GHCR
uses: docker/login-action@v3
Expand All @@ -102,7 +48,15 @@ jobs:
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
labels: ${{ steps.meta.outputs.labels }}

- name: Image Summary
run: |
echo "### 🐳 Docker Image Built" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Tags pushed:**" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
89 changes: 89 additions & 0 deletions .github/workflows/update-manifest.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# GitHub Actions Workflow Template for GitOps with ArgoCD
# This workflow should replace the old deploy.yaml in each microservice repo

name: Update K8s Manifest

on:
workflow_run:
workflows: ["Build and Push Docker Image"]
types: [completed]
branches: ['main', 'dev']

jobs:
update-manifest:
name: Update Image Tag in k8s-config
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest

steps:
- name: Get branch and SHA info
id: info
run: |
BRANCH="${{ github.event.workflow_run.head_branch }}"
SHORT_SHA="$(echo ${{ github.event.workflow_run.head_sha }} | cut -c1-7)"
echo "branch=${BRANCH}" >> $GITHUB_OUTPUT
echo "sha=${SHORT_SHA}" >> $GITHUB_OUTPUT
echo "📍 Branch: ${BRANCH}, SHA: ${SHORT_SHA}"

- name: Checkout k8s-config repo (matching branch)
uses: actions/checkout@v4
with:
repository: 'TechTorque-2025/k8s-config'
token: ${{ secrets.REPO_ACCESS_TOKEN }}
ref: ${{ steps.info.outputs.branch }} # Checkout dev or main to match microservice branch
path: 'k8s-config'

- name: Install yq (YAML processor)
run: |
sudo wget -qO /usr/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64
sudo chmod +x /usr/bin/yq

- name: Update image tag in deployment manifest
env:
SERVICE_NAME: "api_gateway" # e.g., "timelogging_service", "frontend_web", "authentication"
DEPLOYMENT_FILE: "gateway-deployment.yaml" # e.g., "timelogging-deployment.yaml", "frontend-deployment.yaml"
run: |
cd k8s-config
NEW_IMAGE="ghcr.io/techtorque-2025/${SERVICE_NAME}:${{ steps.info.outputs.branch }}-${{ steps.info.outputs.sha }}"
export NEW_IMAGE

echo "🔄 Updating ${DEPLOYMENT_FILE} to use image: ${NEW_IMAGE}"

yq eval -i \
'(select(.kind == "Deployment") | .spec.template.spec.containers[0].image) = env(NEW_IMAGE)' \
k8s/services/${DEPLOYMENT_FILE}

echo "✅ Updated manifest:"
yq eval 'select(.kind == "Deployment") | .spec.template.spec.containers[0].image' k8s/services/${DEPLOYMENT_FILE}

- name: Commit and push changes
env:
SERVICE_NAME: "api_gateway"
run: |
cd k8s-config
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

git add k8s/services/

if git diff --cached --quiet; then
echo "⚠️ No changes detected, skipping commit"
exit 0
fi

git commit -m "chore(${SERVICE_NAME}): update image to ${{ steps.info.outputs.branch }}-${{ steps.info.outputs.sha }}" \
-m "Triggered by: ${{ github.event.workflow_run.html_url }}"

git push origin ${{ steps.info.outputs.branch }}

echo "✅ Pushed manifest update to k8s-config/${{ steps.info.outputs.branch }}"
echo "🚀 ArgoCD will automatically deploy this change"

- name: Summary
run: |
echo "### 🎉 Manifest Update Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Branch**: ${{ steps.info.outputs.branch }}" >> $GITHUB_STEP_SUMMARY
echo "- **Image Tag**: ${{ steps.info.outputs.branch }}-${{ steps.info.outputs.sha }}" >> $GITHUB_STEP_SUMMARY
echo "- **Manifest Updated**: k8s/services/gateway-deployment.yaml" >> $GITHUB_STEP_SUMMARY
echo "- **Next Step**: ArgoCD will sync this change to the cluster" >> $GITHUB_STEP_SUMMARY
15 changes: 15 additions & 0 deletions cmd/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,21 @@ func newProxy(targetURL, stripPrefix string) (*httputil.ReverseProxy, error) {
proxy := httputil.NewSingleHostReverseProxy(target)
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
// Store the custom headers before calling originalDirector
userSubject := req.Header.Get("X-User-Subject")
userRoles := req.Header.Get("X-User-Roles")

originalDirector(req)
req.Host = target.Host

// Re-apply custom headers after originalDirector (which may create a new header map)
if userSubject != "" {
req.Header.Set("X-User-Subject", userSubject)
}
if userRoles != "" {
req.Header.Set("X-User-Roles", userRoles)
}

if stripPrefix != "" {
req.URL.Path = strings.TrimPrefix(req.URL.Path, stripPrefix)
}
Expand Down Expand Up @@ -182,6 +195,8 @@ func injectUserInfo(next http.Handler) http.Handler {
}
logger.Info("Injecting user info into headers", "subject", r.Header.Get("X-User-Subject"), "roles", r.Header.Get("X-User-Roles"))
}
// NOTE: The Authorization header is already present in r.Header and will be
// automatically forwarded by the reverse proxy to downstream services
next.ServeHTTP(w, r)
})
}
Expand Down
2 changes: 1 addition & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,6 @@ services:
- name: "ai"
path_prefix: "/api/v1/ai/"
target_url: "http://localhost:8091" # Agent Bot service (Python FastAPI)
strip_prefix: "" # Don't strip - Agent Bot expects the full path
strip_prefix: "/api/v1/ai" # Strip to match Agent Bot's internal routing
auth_required: true
env_var: "AGENT_BOT_SERVICE_URL"