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
16 changes: 16 additions & 0 deletions .githooks/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh

commit_message_file=$1
commit_message=$(cat "$commit_message_file")

# Conventional commit pattern: type(scope?): description
conventional_regex="^(feat|fix|docs|build|style|refactor|perf|test|chore)(\([a-z0-9_-]+\))?: .+"

if ! echo "$commit_message" | grep -Eq "$conventional_regex"; then
echo "Invalid commit message format: type(scope?): description. While (scope) is optional."
echo "Example: docs(openapi): update API documentation"
echo "Allowed types: feat, fix, docs, build, style, refactor, perf, test, chore"
exit 1
fi

exit 0
16 changes: 16 additions & 0 deletions .githooks/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh

branch_name=$(git rev-parse --abbrev-ref HEAD)

# Allowed format: type/feature-name (kebab-case)
branch_regex="^(feat|fix|build|chore|refactor|docs|perf|test)/[a-z0-9]+(-[a-z0-9]+)*$"

if ! echo "$branch_name" | grep -Eq "$branch_regex"; then
echo "Invalid branch name. Expected format: type/feature-name"
echo "Allowed types: feat, fix, build, chore, refactor, docs, perf, test"
echo "Example: feat/login-page"
echo "Use 'git branch -m new-branch-name' to rename your branch and try again."
exit 1
fi

exit 0
5 changes: 1 addition & 4 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
* @AhmedSobhy01
* @AmrSamy59
* @im-saif
* @LoayAhmed304
* @AhmedSobhy01 @LoayAhmed304 @AmrSamy59 @im-saif
74 changes: 74 additions & 0 deletions .github/workflows/pr-validation-workflow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: Pull Request Validations

on:
pull_request:
types: [opened, edited, reopened, ready_for_review]
branches: [main, dev]

permissions:
pull-requests: read
contents: read

jobs:
validate-merge-permissions:
if: github.event.pull_request.base.ref == 'main'
runs-on: ubuntu-latest
name: Validate Merge Permissions
steps:
- name: Verify Authorized User
run: |
TARGET_BRANCH="${{ github.event.pull_request.base.ref }}"
PR_AUTHOR="${{ github.actor }}"
ALLOWED_USER="AhmedSobhy01"

if [ "$PR_AUTHOR" != "$ALLOWED_USER" ] && [ "$TARGET_BRANCH" = "main" ]; then
echo "Unauthorized PR to main by $PR_AUTHOR, only $ALLOWED_USER is allowed."
exit 1
fi

echo "Authorized PR to main branch."

branch-name-validation:
if: github.event.pull_request.base.ref == 'dev'
name: Validate branch naming convention
runs-on: ubuntu-latest

steps:
- name: Enforce kebab-case branch name
env:
BRANCH_NAME: ${{ github.head_ref }}
BRANCH_PATTERN: '^(feat|fix|build|chore|refactor|docs|perf|test)/[a-z0-9]+(-[a-z0-9]+)*$'
run: |
echo "Checking branch: $BRANCH_NAME"
if ! echo "$BRANCH_NAME" | grep -Eq "$BRANCH_PATTERN"; then
echo "Branch name doesn't match the required pattern. Please setup your git hooks locally to avoid this error."
exit 1
fi

echo "Branch name passed."

validate-pr-title:
name: Validate PR title format
runs-on: ubuntu-latest

steps:
- name: Check PR title format
env:
PR_TITLE: ${{github.event.pull_request.title}}
PREFIX_PATTERN: '^(feat|fix|build|chore|refactor|docs|perf|test)(\(.+\))?: .+'
POSTFIX_PATTERN: " - \\[CU-[a-zA-Z0-9]+\\]$"
run: |
CLEAN_TITLE=$(echo "$PR_TITLE" | tr -d '\r' | sed 's/[[:space:]]\+/ /g' | sed 's/^ *//;s/ *$//') # Normalize spaces

echo "Checking PR title: $CLEAN_TITLE"
if ! echo "$CLEAN_TITLE" | grep -Eq "$PREFIX_PATTERN"; then
echo "PR title must start with a valid prefix (feat, fix, build, chore, refactor, docs, perf, test) followed by a colon and a space."
exit 1
fi

if ! echo "$CLEAN_TITLE" | grep -Eq "$POSTFIX_PATTERN"; then
echo "PR title must end with a valid Clickup PR ticket. (e.g., ' - [CU-1234]')"
exit 1
fi

echo "PR title passed."
34 changes: 34 additions & 0 deletions docker-composes/logging/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
services:
watchtower:
image: containrrr/watchtower
container_name: watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.
env_file:
- .env
environment:
- WATCHTOWER_POLL_INTERVAL=800
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_LABEL_ENABLE=true
- REPO_USER=loayahmed
- WATCHTOWER_NOTIFICATIONS=shoutrrr
- WATCHTOWER_NOTIFICATION_TITLETAG=Raven
command: --interval 800 --cleanup --label-enable --debug
restart: unless-stopped

dozzle:
image: amir20/dozzle:latest
container_name: dozzle
ports:
- '6677:8080'
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./users/:/data/
environment:
DOZZLE_HOSTNAME: Raven
DOZZLE_AUTH_PROVIDER: simple
DOZZLE_AUTH_TTL: 48h
DOZZLE_ENABLE_ACTIONS: true
DOZZLE_ENABLE_SHELL: true

restart: unless-stopped
73 changes: 73 additions & 0 deletions docker-composes/production/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
services:
frontend:
image: loayahmed/raven-frontend:latest
container_name: frontend-production
labels:
- 'com.centurylinklabs.watchtower.enable=true'
ports:
- ${FRONTEND_PORT}:3000
env_file:
- .env
environment:
BACKEND_URL: ${BACKEND_URL}
depends_on:
- backend
networks:
- default-prod
restart: on-failure

load_balancer:
image: nginx:alpine
container_name: load-balancer
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- ${BACKEND_PORT}:80
depends_on:
- backend
networks:
- default-prod
restart: unless-stopped

backend:
image: loayahmed/raven-backend:latest
labels:
- 'com.centurylinklabs.watchtower.enable=true'
env_file:
- .env
environment:
PORT: ${BACKEND_PORT}
depends_on:
- redis
networks:
- default-prod
restart: on-failure
healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:5000/health']
interval: 30s
timeout: 5s
retries: 3
start_period: 10s

redis:
image: redis:8-alpine
container_name: redis-production
volumes:
- redisdata-prod:/data
command: ['redis-server', '--appendonly', 'yes']
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 3
networks:
- default-prod
restart: unless-stopped

volumes:
redisdata-prod:

networks:
default-prod:
driver: bridge
name: raven-network-prod
36 changes: 36 additions & 0 deletions docker-composes/production/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
events {
worker_connections auto;
}

http {
upstream backend_upstream {
ip_hash; # for sticky sessions
server production-backend-1:5000;
server production-backend-2:5000;
server production-backend-3:5000;

}

server {
listen 80;
server_name api.raven.cmp27.space;

location / {
proxy_pass http://backend_upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;


# websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# prevent timeout
proxy_read_timeout 3600;
proxy_send_timeout 3600;
}
}
}
69 changes: 69 additions & 0 deletions docker-composes/staging/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
services:
frontend:
image: loayahmed/raven-frontend:staging-latest
container_name: frontend-staging
labels:
- 'com.centurylinklabs.watchtower.enable=true'
ports:
- ${FRONTEND_PORT}:3000
env_file:
- .env
depends_on:
- backend
networks:
- default
restart: on-failure

backend:
image: loayahmed/raven-backend:staging-latest
container_name: backend-staging
ports:
- ${BACKEND_PORT}:8000
logging:
driver: json-file
options:
max-size: '20m'
max-file: '3'
labels:
- 'com.centurylinklabs.watchtower.enable=true'
env_file:
- .env
environment:
PORT: ${BACKEND_PORT}
depends_on:
- redis
networks:
- default
restart: on-failure

healthcheck:
test: ['CMD', 'wget', '--spider', '-q', 'http://localhost:8000/health']
interval: 30s
timeout: 5s
retries: 3
start_period: 10s

redis:
image: redis:8-alpine
container_name: redis
ports:
- ${REDIS_PORT}:6379
volumes:
- redisdata:/data
command: ['redis-server', '--appendonly', 'yes']
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 10s
timeout: 5s
retries: 3
networks:
- default
restart: unless-stopped

volumes:
redisdata:

networks:
default:
driver: bridge
name: raven-network-staging
Loading
Loading