almosther isolating soleprint

This commit is contained in:
buenosairesam
2025-12-24 07:10:08 -03:00
parent d62337e7ba
commit 33ee1b44cd
28 changed files with 1417 additions and 114 deletions

View File

@@ -0,0 +1,157 @@
# Pawprint Control Scripts
Control scripts for managing pawprint services via systemd (alternative to Docker deployment).
## Structure
```
ctrl/
├── .env.pawprint # Shared configuration
├── local/ # Scripts run from developer machine
│ ├── commit.sh # Commit changes across all repos
│ ├── deploy.sh # Full deployment workflow
│ ├── init.sh # Initial sync to server
│ ├── push.sh # Deploy to server (all by default)
│ └── status.sh # Git status of all repos
└── server/ # Scripts run on server
├── install-deps.sh # Install Python deps (all by default)
├── restart.sh # Restart services (all by default)
├── setup-cert.sh # Setup SSL certificate
├── setup-nginx.sh # Create nginx config
└── setup-service.sh # Create systemd service
```
## Configuration
Edit `.env.pawprint` to configure:
```bash
# Deployment
DEPLOY_SERVER=mariano@mcrn.ar
DEPLOY_REMOTE_PATH=~/pawprint
# Local paths
PAWPRINT_BARE_PATH=/home/mariano/pawprint
# Server paths
SERVER_USER=mariano
SERVER_PAWPRINT_PATH=/home/mariano/pawprint
SERVER_VENV_BASE=/home/mariano/venvs
```
## Design Principle
**All services are the default.** No flags needed for common operations.
```bash
./push.sh # Deploys all (default)
./push.sh artery # Deploy only artery (when needed)
```
See `DESIGN_PAWPRINT.md` for detailed philosophy.
## Local Scripts
### commit.sh
```bash
./local/commit.sh "Your commit message"
```
### status.sh
```bash
./local/status.sh
```
### push.sh
```bash
./local/push.sh # Push all services (default)
./local/push.sh artery # Push only artery
```
### deploy.sh
```bash
./local/deploy.sh
# Then restart on server:
# ssh mariano@mcrn.ar 'bash ~/pawprint/ctrl/server/restart.sh'
```
### init.sh
```bash
./local/init.sh # Initial full sync (run once)
```
## Server Scripts
### restart.sh
```bash
sudo ./server/restart.sh # Restart all (default)
sudo ./server/restart.sh artery # Restart only artery
```
### install-deps.sh
```bash
./server/install-deps.sh # Install all (default)
./server/install-deps.sh artery # Install only artery
```
### setup-service.sh
```bash
sudo ./server/setup-service.sh pawprint 12000 main:app
sudo ./server/setup-service.sh artery 12001 main:app
```
### setup-nginx.sh
```bash
sudo ./server/setup-nginx.sh artery artery.mcrn.ar 12001
```
### setup-cert.sh
```bash
sudo ./server/setup-cert.sh artery.mcrn.ar
```
## Deployment Workflow
### Initial Setup (once)
Local:
```bash
cd ctrl/local
./init.sh
```
Server:
```bash
cd ~/pawprint/ctrl/server
./install-deps.sh
sudo ./setup-service.sh pawprint 12000 main:app
sudo ./setup-service.sh artery 12001 main:app
sudo ./setup-service.sh album 12002 main:app
sudo ./setup-service.sh ward 12003 main:app
sudo ./setup-nginx.sh pawprint pawprint.mcrn.ar 12000
sudo ./setup-nginx.sh artery artery.mcrn.ar 12001
sudo ./setup-nginx.sh album album.mcrn.ar 12002
sudo ./setup-nginx.sh ward ward.mcrn.ar 12003
```
### Regular Updates
Local:
```bash
cd ctrl/local
./commit.sh "Update feature X"
./deploy.sh
```
Server:
```bash
sudo ~/pawprint/ctrl/server/restart.sh
```
## Nest vs Pawprint Control
- **core_nest/ctrl/** - Manages full nest (amar + pawprint) via Docker
- **pawprint/ctrl/** - Manages pawprint services via systemd
This directory provides systemd-based deployment as an alternative to Docker.
For full nest orchestration with Docker, use `core_nest/ctrl/`.

View File

@@ -0,0 +1,34 @@
#!/bin/bash
# Commit changes across all repos with the same message
# Usage: ./commit.sh "commit message"
set -e
MSG="${1:?Usage: $0 \"commit message\"}"
# Find pawprint bare metal directory from PAWPRINT_BARE_PATH or default
PAWPRINT_DIR="${PAWPRINT_BARE_PATH:-/home/mariano/pawprint}"
REPOS=("$PAWPRINT_DIR" "$PAWPRINT_DIR/artery" "$PAWPRINT_DIR/album" "$PAWPRINT_DIR/ward")
for repo in "${REPOS[@]}"; do
name=$(basename "$repo")
[ "$repo" = "$PAWPRINT_DIR" ] && name="pawprint"
if [ ! -d "$repo/.git" ]; then
echo "=== $name: not a git repo, skipping ==="
continue
fi
cd "$repo"
if git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
echo "=== $name: nothing to commit ==="
continue
fi
echo "=== $name ==="
git add -A
git commit -m "$MSG"
done
echo "Done!"

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Push all to server (run locally)
# Usage: ./deploy.sh
# Then run restart on server as admin
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CTRL_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Load configuration
source "$CTRL_DIR/.env.soleprint" 2>/dev/null || true
REMOTE="${DEPLOY_SERVER:-mariano@mcrn.ar}"
echo "=== Pushing all ==="
"$SCRIPT_DIR/push.sh"
echo ""
echo "=== Push complete ==="
echo "Now restart services on server:"
echo " ssh $REMOTE 'sudo systemctl restart soleprint artery atlas station'"
echo ""
echo "# Or restart specific service:"
echo "# ssh $REMOTE 'sudo systemctl restart artery'"

View File

@@ -0,0 +1,28 @@
#!/bin/bash
# Initial full sync of pawprint to server
# Run once to setup, then use push.sh for updates
set -e
# Load configuration
CTRL_DIR="$(cd "$(dirname "$0")/../.." SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" pwd)"
source "$CTRL_DIR/.env.pawprint" 2>/dev/null || true
PAWPRINT_DIR="${PAWPRINT_BARE_PATH:-/home/mariano/pawprint}"
REMOTE="${DEPLOY_SERVER:-mariano@mcrn.ar}"
REMOTE_DIR="${DEPLOY_REMOTE_PATH:-~/pawprint}"
echo "=== Initial sync of pawprint ==="
echo "From: $PAWPRINT_DIR"
echo "To: $REMOTE:$REMOTE_DIR"
rsync -avz \
--filter=':- .gitignore' \
--exclude '.git' \
--exclude '.env' \
"$PAWPRINT_DIR/" "$REMOTE:$REMOTE_DIR/"
echo ""
echo "Done! Now on server run:"
echo " cd ~/pawprint"
echo " # Use core_nest/pawprint/tools/server/setup-*.sh scripts for initial setup"

View File

@@ -0,0 +1,66 @@
#!/bin/bash
# Deploy repos via rsync
# Usage: ./push.sh [target]
# Example: ./push.sh (deploys all: soleprint, artery, atlas, station)
# ./push.sh artery (deploys only artery)
# ./push.sh soleprint (deploys only soleprint root, no sub-repos)
set -e
TARGET="${1:-all}"
# Load configuration
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CTRL_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "$CTRL_DIR/.env.soleprint" 2>/dev/null || true
SOLEPRINT_DIR="${SOLEPRINT_BARE_PATH:-/home/mariano/wdir/spr/gen}"
REMOTE="${DEPLOY_SERVER:-mariano@mcrn.ar}"
REMOTE_BASE="${DEPLOY_REMOTE_PATH:-~/soleprint}"
# Handle all (default)
if [ "$TARGET" = "all" ]; then
echo "=== Deploying all services ==="
for target in soleprint artery atlas station; do
"$0" "$target"
echo ""
done
echo "=== All done ==="
exit 0
fi
if [ "$TARGET" = "soleprint" ]; then
# Push only root files (no sub-repos)
echo "=== Deploying soleprint (root only) ==="
rsync -avz \
--filter=':- .gitignore' \
--exclude '.git' \
--exclude '.env' \
--exclude '.venv' \
--exclude 'artery/' \
--exclude 'atlas/' \
--exclude 'station/' \
"$SOLEPRINT_DIR/" "$REMOTE:$REMOTE_BASE/"
echo "Done!"
exit 0
fi
LOCAL_DIR="$SOLEPRINT_DIR/$TARGET"
REMOTE_DIR="$REMOTE_BASE/$TARGET"
if [ ! -d "$LOCAL_DIR" ]; then
echo "Error: $LOCAL_DIR does not exist"
exit 1
fi
echo "=== Deploying $TARGET ==="
echo "From: $LOCAL_DIR"
echo "To: $REMOTE:$REMOTE_DIR"
rsync -avz \
--filter=':- .gitignore' \
--exclude '.git' \
--exclude '.env' \
"$LOCAL_DIR/" "$REMOTE:$REMOTE_DIR/"
echo "Done!"

View File

@@ -0,0 +1,33 @@
#!/bin/bash
# Show git status of all repos
# Usage: ./status.sh
# Find pawprint bare metal directory from PAWPRINT_BARE_PATH or default
PAWPRINT_DIR="${PAWPRINT_BARE_PATH:-/home/mariano/pawprint}"
REPOS=("$PAWPRINT_DIR" "$PAWPRINT_DIR/artery" "$PAWPRINT_DIR/album" "$PAWPRINT_DIR/ward")
for repo in "${REPOS[@]}"; do
name=$(basename "$repo")
[ "$repo" = "$PAWPRINT_DIR" ] && name="pawprint"
if [ ! -d "$repo/.git" ]; then
echo "=== $name: not a git repo ==="
continue
fi
cd "$repo"
branch=$(git branch --show-current)
# Check for changes
staged=$(git diff --cached --numstat | wc -l)
unstaged=$(git diff --numstat | wc -l)
untracked=$(git ls-files --others --exclude-standard | wc -l)
if [ "$staged" -eq 0 ] && [ "$unstaged" -eq 0 ] && [ "$untracked" -eq 0 ]; then
echo "=== $name ($branch): clean ==="
else
echo "=== $name ($branch): +$staged staged, ~$unstaged modified, ?$untracked untracked ==="
git status --short
fi
echo
done

View File

@@ -0,0 +1,55 @@
#!/bin/bash
# Install/update dependencies for apps
# Usage: ./install-deps.sh [app-name]
# Example: ./install-deps.sh (installs deps for all services)
# ./install-deps.sh artery (installs deps for artery only)
set -e
APP_NAME="${1:-all}"
# Load configuration
CTRL_DIR="$(cd "$(dirname "$0")/../.." SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" pwd)"
source "$CTRL_DIR/.env.pawprint" 2>/dev/null || true
APP_USER="${SERVER_USER:-mariano}"
PAWPRINT_PATH="${SERVER_PAWPRINT_PATH:-/home/mariano/pawprint}"
VENV_BASE="${SERVER_VENV_BASE:-/home/mariano/venvs}"
# Handle all (default)
if [ "$APP_NAME" = "all" ]; then
echo "=== Installing deps for all services ==="
for app in pawprint artery album ward; do
echo ""
echo "--- $app ---"
"$0" "$app"
done
echo ""
echo "=== All done ==="
exit 0
fi
VENV_DIR="$VENV_BASE/$APP_NAME"
if [ "$APP_NAME" = "pawprint" ]; then
REQ_FILE="$PAWPRINT_PATH/requirements.txt"
else
REQ_FILE="$PAWPRINT_PATH/$APP_NAME/requirements.txt"
fi
if [ ! -f "$REQ_FILE" ]; then
echo "Error: $REQ_FILE not found"
exit 1
fi
if [ ! -d "$VENV_DIR" ]; then
echo "Creating venv: $VENV_DIR"
python3 -m venv "$VENV_DIR"
fi
echo "Installing deps from $REQ_FILE"
source "$VENV_DIR/bin/activate"
pip install -r "$REQ_FILE"
deactivate
echo "Done!"

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Restart pawprint services
# Usage: ./restart.sh [service]
# Example: ./restart.sh (restarts all services)
# ./restart.sh artery (restarts only artery)
set -e
TARGET="${1:-all}"
# Handle all (default)
if [ "$TARGET" = "all" ]; then
echo "Restarting all services..."
systemctl restart pawprint artery album ward
echo "Status:"
systemctl status pawprint artery album ward --no-pager | grep -E "●|Active:"
exit 0
fi
echo "Restarting $TARGET..."
systemctl restart "$TARGET"
echo "Status:"
systemctl status "$TARGET" --no-pager | grep -E "●|Active:"

View File

@@ -0,0 +1,24 @@
#!/bin/bash
# Install/update SSL certificate for a subdomain
# Usage: ./setup-cert.sh <subdomain>
# Example: ./setup-cert.sh pawprint.mcrn.ar
set -e
SUBDOMAIN="${1:?Usage: $0 <subdomain>}"
echo "=== Setting up SSL cert for $SUBDOMAIN ==="
# Check if certbot is installed
if ! command -v certbot &> /dev/null; then
echo "Installing certbot..."
apt update
apt install -y certbot python3-certbot-nginx
fi
# Get/renew certificate
certbot --nginx -d "$SUBDOMAIN" --non-interactive --agree-tos --register-unsafely-without-email
echo ""
echo "Done! Certificate installed for $SUBDOMAIN"
echo "Auto-renewal is enabled via systemd timer"

View File

@@ -0,0 +1,54 @@
#!/bin/bash
# Creates nginx config for FastAPI app
# Usage: ./setup-nginx.sh <app-name> <subdomain> <port>
# Example: ./setup-nginx.sh artery artery.mcrn.ar 12001
set -e
APP_NAME="${1:?Usage: $0 <app-name> <subdomain> <port>}"
SUBDOMAIN="${2:?Usage: $0 <app-name> <subdomain> <port>}"
PORT="${3:?Usage: $0 <app-name> <subdomain> <port>}"
NGINX_CONF="/etc/nginx/sites-available/$APP_NAME"
echo "Creating nginx config: $NGINX_CONF"
sudo tee "$NGINX_CONF" > /dev/null << EOF
server {
listen 80;
server_name $SUBDOMAIN;
return 301 https://\$host\$request_uri;
}
server {
listen 443 ssl;
server_name $SUBDOMAIN;
ssl_certificate /etc/letsencrypt/live/mcrn.ar/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mcrn.ar/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://127.0.0.1:$PORT;
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;
proxy_read_timeout 300;
}
}
EOF
echo "Enabling site..."
sudo ln -sf "$NGINX_CONF" /etc/nginx/sites-enabled/
echo "Testing nginx config..."
sudo nginx -t
echo "Reloading nginx..."
sudo systemctl reload nginx
echo ""
echo "Done! Site available at https://$SUBDOMAIN"
echo "Note: Make sure DNS points $SUBDOMAIN to this server"

View File

@@ -0,0 +1,54 @@
#!/bin/bash
# Creates systemd service for FastAPI app
# Usage: ./setup-service.sh <app-name> <port> <app-module>
# Example: ./setup-service.sh artery 12001 main:app
set -e
APP_NAME="${1:?Usage: $0 <app-name> <port> <app-module>}"
PORT="${2:?Usage: $0 <app-name> <port> <app-module>}"
APP_MODULE="${3:-main:app}"
APP_USER="mariano"
VENV_DIR="/home/$APP_USER/venvs/$APP_NAME"
# pawprint root is special case
if [ "$APP_NAME" = "pawprint" ]; then
WORK_DIR="/home/$APP_USER/pawprint"
else
WORK_DIR="/home/$APP_USER/pawprint/$APP_NAME"
fi
SERVICE_FILE="/etc/systemd/system/${APP_NAME}.service"
echo "Creating systemd service: $SERVICE_FILE"
sudo tee "$SERVICE_FILE" > /dev/null << EOF
[Unit]
Description=$APP_NAME FastAPI service
After=network.target
[Service]
User=$APP_USER
Group=$APP_USER
WorkingDirectory=$WORK_DIR
Environment="PATH=$VENV_DIR/bin"
EnvironmentFile=$PAWPRINT_PATH/.env
ExecStart=$VENV_DIR/bin/uvicorn $APP_MODULE --host 127.0.0.1 --port $PORT
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
echo "Reloading systemd..."
sudo systemctl daemon-reload
echo "Enabling service..."
sudo systemctl enable "$APP_NAME"
echo ""
echo "Done! Service commands:"
echo " sudo systemctl start $APP_NAME"
echo " sudo systemctl status $APP_NAME"
echo " sudo journalctl -u $APP_NAME -f"

View File

@@ -0,0 +1,82 @@
#!/bin/bash
#
# Sync contract tests from amar_django_back_contracts to ward/tools/tester
#
# Usage: ./sync-tests.sh
set -e
# Paths
SOURCE_REPO="/home/mariano/wdir/ama/amar_django_back_contracts"
DEST_DIR="/home/mariano/wdir/ama/pawprint/ward/tools/tester/tests"
# Colors
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${BLUE}=== Syncing Contract Tests ===${NC}"
echo "Source: $SOURCE_REPO/tests/contracts"
echo "Dest: $DEST_DIR"
echo
# Check source exists
if [ ! -d "$SOURCE_REPO/tests/contracts" ]; then
echo "Error: Source directory not found: $SOURCE_REPO/tests/contracts"
exit 1
fi
# Create destination if it doesn't exist
mkdir -p "$DEST_DIR"
# Sync test files (preserve structure)
echo -e "${BLUE}Copying test files...${NC}"
# Copy the contract test structure
rsync -av --delete \
--include="*/" \
--include="test_*.py" \
--include="__init__.py" \
--include="base*.py" \
--include="conftest.py" \
--include="endpoints.py" \
--include="helpers.py" \
--exclude="*" \
"$SOURCE_REPO/tests/contracts/" \
"$DEST_DIR/"
# Remove base_api.py and base_live.py (we only need pure HTTP base.py)
rm -f "$DEST_DIR/base_api.py" "$DEST_DIR/base_live.py"
# Create a simple base.py that uses tester's base class
cat > "$DEST_DIR/base.py" << 'EOF'
"""
Contract Tests - Base Class
Uses tester's HTTP base class for framework-agnostic testing.
"""
# Import from tester's base
import sys
from pathlib import Path
# Add tester to path if needed
tester_path = Path(__file__).parent.parent
if str(tester_path) not in sys.path:
sys.path.insert(0, str(tester_path))
from base import ContractTestCase
__all__ = ["ContractTestCase"]
EOF
echo
echo -e "${GREEN}✓ Tests synced successfully${NC}"
echo
echo "Test structure:"
find "$DEST_DIR" -name "test_*.py" -type f | sed 's|'"$DEST_DIR"'||' | sort
echo
echo -e "${BLUE}Next steps:${NC}"
echo "1. Run tester locally: cd /home/mariano/wdir/ama/pawprint/ward && python -m tools.tester"
echo "2. Deploy to server: cd /home/mariano/wdir/ama/pawprint/deploy && ./deploy.sh"