Skip to content
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
18 changes: 14 additions & 4 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
[alias]
dev-auth = ["watch", "-c", "-w", "services/auth-service", "-w", "libs/shared", "-x", "run -p auth-service"]
dev-urls = ["watch", "-c", "-w", "services/url-service", "-w", "libs/shared", "-x", "run -p url-service"]
dev-auth = ["watch", "-c", "-w", "services/auth-service", "-w", "libs/shared", "-x", "run -p auth-service"]
dev-urls = ["watch", "-c", "-w", "services/url-service", "-w", "libs/shared", "-x", "run -p url-service"]
dev-consumer = ["watch", "-c", "-w", "services/consumer-service", "-w", "libs/shared", "-x", "run -p consumer-service"]

# Run tests with correct DATABASE_URL per service
test-auth = ["test", "-p", "auth-service"]
test-urls = ["test", "-p", "url-service"]

# Kubernetes cluster lifecycle (mirrors docker compose up/down)
# Usage: cargo k8s up | start | stop | down | purge | deploy | status
k8s = ["run", "--manifest-path", "/dev/null", "--"]

[env]
# Provide a default DATABASE_URL for `sqlx::test` across the workspace
DATABASE_URL = "postgres://bittu:bittu@localhost:5432/bittuly_auth"
# Default DATABASE_URL used by `#[sqlx::test]` in auth-service.
# For url-service repository tests, override: DATABASE_URL=postgres://bittu:bittu@localhost:5433/bittuly_urls cargo test -p url-service
# Or use the convenience script: ./scripts/test.sh
DATABASE_URL = { value = "postgres://bittu:bittu@localhost:5432/bittuly_auth", force = false }
137 changes: 137 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
name: Bittuly CD

on:
push:
branches: ["main"]

env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ghcr.io/${{ github.repository_owner }}

jobs:
# ── Build & push all Docker images to GHCR ───────────────────────────────
build-and-push:
name: Build & Push Images
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

outputs:
image-tag: ${{ steps.meta.outputs.version }}

steps:
- uses: actions/checkout@v4

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

# Generate image tag from git SHA (short) + branch
- name: Generate image tag
id: meta
run: echo "version=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"

- name: Build & push auth-service
uses: docker/build-push-action@v6
with:
context: .
file: services/auth-service/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/bittuly-auth-service:${{ steps.meta.outputs.version }}
${{ env.IMAGE_PREFIX }}/bittuly-auth-service:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build & push url-service
uses: docker/build-push-action@v6
with:
context: .
file: services/url-service/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/bittuly-url-service:${{ steps.meta.outputs.version }}
${{ env.IMAGE_PREFIX }}/bittuly-url-service:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build & push consumer-service
uses: docker/build-push-action@v6
with:
context: .
file: services/consumer-service/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/bittuly-consumer-service:${{ steps.meta.outputs.version }}
${{ env.IMAGE_PREFIX }}/bittuly-consumer-service:latest
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build & push frontend-service
uses: docker/build-push-action@v6
with:
context: ./web
file: web/Dockerfile
push: true
tags: |
${{ env.IMAGE_PREFIX }}/bittuly-frontend-service:${{ steps.meta.outputs.version }}
${{ env.IMAGE_PREFIX }}/bittuly-frontend-service:latest
cache-from: type=gha
cache-to: type=gha,mode=max

# ── Update k8s manifests with new image tags ─────────────────────────────
# Uncomment and configure when you have a production cluster.
# deploy:
# name: Deploy to Production
# runs-on: ubuntu-latest
# needs: build-and-push
# environment: production # requires manual approval in GitHub UI
#
# steps:
# - uses: actions/checkout@v4
#
# # Option A: Direct kubectl (simple, for single cluster)
# - name: Set up kubectl
# uses: azure/setup-kubectl@v4
#
# - name: Configure kubeconfig
# run: echo "${{ secrets.KUBECONFIG }}" | base64 -d > ~/.kube/config
#
# - name: Update image tags in manifests
# run: |
# TAG=${{ needs.build-and-push.outputs.image-tag }}
# REGISTRY=${{ env.IMAGE_PREFIX }}
# kubectl set image deployment/auth-service \
# auth-service=${REGISTRY}/bittuly-auth-service:${TAG} -n bittuly
# kubectl set image deployment/url-service \
# url-service=${REGISTRY}/bittuly-url-service:${TAG} -n bittuly
# kubectl set image deployment/consumer-service \
# consumer-service=${REGISTRY}/bittuly-consumer-service:${TAG} -n bittuly
# kubectl set image deployment/frontend-service \
# frontend-service=${REGISTRY}/bittuly-frontend-service:${TAG} -n bittuly
#
# - name: Wait for rollout
# run: |
# kubectl rollout status deployment/auth-service -n bittuly --timeout=120s
# kubectl rollout status deployment/url-service -n bittuly --timeout=120s
# kubectl rollout status deployment/consumer-service -n bittuly --timeout=120s
# kubectl rollout status deployment/frontend-service -n bittuly --timeout=120s
#
# # Option B: ArgoCD (GitOps β€” recommended for production)
# # Update image tag in the manifest file, commit, ArgoCD auto-syncs.
# # - name: Update manifest image tag
# # run: |
# # TAG=${{ needs.build-and-push.outputs.image-tag }}
# # sed -i "s|image: .*bittuly-auth-service:.*|image: ${{ env.IMAGE_PREFIX }}/bittuly-auth-service:${TAG}|" \
# # k8s/base/auth-service/auth-service.yaml
# # git config user.name "github-actions[bot]"
# # git config user.email "github-actions[bot]@users.noreply.github.com"
# # git commit -am "chore(cd): bump images to ${TAG}"
# # git push
179 changes: 157 additions & 22 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,63 +2,198 @@ name: Bittuly CI

on:
push:
branches: [ "main", "dist_sys" ]
branches: ["main", "dev"]
pull_request:
branches: [ "main", "dist_sys" ]
branches: ["main", "dev"]

env:
CARGO_TERM_COLOR: always

jobs:
backend-checks:
name: Backend (Rust)
# ── Rust: format + lint + audit ──────────────────────────────────────────
rust-lint:
name: Rust β€” Format, Clippy & Audit
runs-on: ubuntu-latest

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

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
components: clippy, rustfmt

- name: Rust Cache
uses: Swatinem/rust-cache@v2
- uses: Swatinem/rust-cache@v2

- name: Check formatting
run: cargo fmt --all -- --check

- name: Clippy Lints
run: cargo clippy --workspace -- -D warnings
- name: Clippy lints
run: cargo clippy --workspace --all-targets -- -D warnings

- name: Run Tests
run: cargo test --workspace
- name: Install cargo-audit
run: cargo install cargo-audit --locked

frontend-checks:
name: Frontend (React)
- name: Security audit
run: cargo audit --ignore RUSTSEC-2023-0071

# ── Rust: auth-service tests (needs postgres-auth) ───────────────────────
test-auth:
name: Test β€” auth-service
runs-on: ubuntu-latest
needs: rust-lint

services:
postgres-auth:
image: postgres:17-alpine
env:
POSTGRES_USER: bittu
POSTGRES_PASSWORD: bittu
POSTGRES_DB: bittuly_auth
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U bittu -d bittuly_auth"
--health-interval 5s
--health-timeout 3s
--health-retries 10

env:
DATABASE_URL: postgres://bittu:bittu@localhost:5432/bittuly_auth
AUTH_DATABASE_URL: postgres://bittu:bittu@localhost:5432/bittuly_auth
JWT_SECRET: ci-test-secret-key
MODE: development

steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

- name: Run auth-service tests
run: cargo test -p auth-service

# ── Rust: url-service tests (needs postgres-urls + redis) ────────────────
test-url:
name: Test β€” url-service
runs-on: ubuntu-latest
needs: rust-lint

services:
postgres-urls:
image: postgres:17-alpine
env:
POSTGRES_USER: bittu
POSTGRES_PASSWORD: bittu
POSTGRES_DB: bittuly_urls
ports:
- 5433:5432
options: >-
--health-cmd "pg_isready -U bittu -d bittuly_urls"
--health-interval 5s
--health-timeout 3s
--health-retries 10

redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s
--health-timeout 3s
--health-retries 10

env:
DATABASE_URL: postgres://bittu:bittu@localhost:5433/bittuly_urls
URL_DATABASE_URL: postgres://bittu:bittu@localhost:5433/bittuly_urls
REDIS_URL: redis://127.0.0.1:6379
RABBITMQ_URL: amqp://guest:guest@localhost:5672
JWT_SECRET: ci-test-secret-key
MODE: development

steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

- name: Run url-service tests
run: cargo test -p url-service

# ── Docker: smoke-build all images ───────────────────────────────────────
docker-build:
name: Docker β€” Smoke Build
runs-on: ubuntu-latest

needs: [test-auth, test-url]

steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build auth-service image
uses: docker/build-push-action@v6
with:
context: .
file: services/auth-service/Dockerfile
push: false
tags: bittuly/auth-service:ci
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build url-service image
uses: docker/build-push-action@v6
with:
context: .
file: services/url-service/Dockerfile
push: false
tags: bittuly/url-service:ci
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build consumer-service image
uses: docker/build-push-action@v6
with:
context: .
file: services/consumer-service/Dockerfile
push: false
tags: bittuly/consumer-service:ci
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Build frontend image
uses: docker/build-push-action@v6
with:
context: ./web
file: web/Dockerfile
push: false
tags: bittuly/frontend-service:ci
cache-from: type=gha
cache-to: type=gha,mode=max

# ── Frontend: typecheck + build ───────────────────────────────────────────
frontend:
name: Frontend β€” Typecheck & Build
runs-on: ubuntu-latest

defaults:
run:
working-directory: ./web

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

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '26'
cache: 'npm'
cache-dependency-path: './web/package-lock.json'
node-version: "20"
cache: "npm"
cache-dependency-path: "./web/package-lock.json"

- name: Install dependencies
run: npm ci

- name: Typecheck
run: npm run typecheck

- name: Verify Build
- name: Build
run: npm run build
Loading
Loading