diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5b0074..e54d16c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,9 @@ jobs: backend: runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -45,21 +48,15 @@ jobs: key: \${{ runner.os }}-go-\${{ hashFiles('**/go.sum') }} restore-keys: | \${{ runner.os }}-go- - - name: Add Go bin to PATH - run: echo "$HOME/go/bin" >> $GITHUB_PATH - - name: Install Go tooling - run: ./scripts/install-go-tools.sh - - name: Lint backend - working-directory: backend - run: golangci-lint run ./... + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + working-directory: backend + only-new-issues: true - name: Run backend tests working-directory: backend run: go test ./... - - name: Build backend plugin (linux/amd64) - working-directory: backend - run: | - mkdir -p build - GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build -trimpath --mod=vendor -buildmode=plugin -o build/backend.so - name: Docker build run: docker build -f backend/Dockerfile -t tictactoe/nakama:ci ./backend - name: Push to Artifact Registry diff --git a/.github/workflows/deploy-backend.yml b/.github/workflows/deploy-backend.yml new file mode 100644 index 0000000..4c63fb3 --- /dev/null +++ b/.github/workflows/deploy-backend.yml @@ -0,0 +1,171 @@ +name: Backend Production Deployment + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to deploy' + required: true + default: 'main' + type: string + environment: + description: 'Deployment environment' + required: true + default: 'production' + type: choice + options: + - production + - staging + +jobs: + deploy: + runs-on: ubuntu-latest + name: Deploy Backend to GCP VM + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get branch name and commit info + id: branch-info + shell: bash + run: | + echo "BRANCH_NAME=${{ github.event.inputs.branch }}" >> $GITHUB_OUTPUT + echo "COMMIT_SHA=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT + echo "COMMIT_SHORT=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Validate backend changes + run: | + echo "๐Ÿ” Validating backend deployment..." + echo "Branch: ${{ steps.branch-info.outputs.BRANCH_NAME }}" + echo "Commit: ${{ steps.branch-info.outputs.COMMIT_SHORT }}" + echo "Environment: ${{ github.event.inputs.environment }}" + + - name: Deploy backend to GCP VM + uses: appleboy/ssh-action@v1 + with: + host: ${{ secrets.GCP_VM_HOST }} + username: ${{ secrets.GCP_VM_SSH_USER }} + key: ${{ secrets.GCP_VM_SSH_KEY }} + port: ${{ secrets.GCP_VM_SSH_PORT || '22' }} + timeout: 300s + script: | + set -euo pipefail + + echo "๐Ÿš€ Starting backend deployment..." + echo "๐Ÿ“… Deployment started at: $(date)" + echo "๐ŸŒฟ Target branch: ${{ steps.branch-info.outputs.BRANCH_NAME }}" + echo "๐Ÿ“ Commit: ${{ steps.branch-info.outputs.COMMIT_SHORT }}" + echo "๐ŸŽฏ Environment: ${{ github.event.inputs.environment }}" + + # Define application directory + APP_DIR="$HOME/tictactoe-game" + BACKEND_DIR="$APP_DIR/backend" + + # Create application directory if it doesn't exist + mkdir -p "$APP_DIR" + + # Clone repository if it doesn't exist, otherwise update + if [ ! -d "$APP_DIR/.git" ]; then + echo "๐Ÿ“ฅ Cloning repository..." + cd "$HOME" + git clone https://github.com/rogdex24/tictactoe-game.git + else + echo "๐Ÿ”„ Updating existing repository..." + cd "$APP_DIR" + fi + + # Ensure we're in the app directory + cd "$APP_DIR" + + + # Stop current services + echo "๐Ÿ›‘ Stopping current backend services..." + cd "$BACKEND_DIR" || cd "$APP_DIR" + if [ -f "docker-compose.yml" ]; then + sudo docker compose down || docker-compose down || true + fi + + # Update repository + echo "๐Ÿ“ก Fetching latest changes..." + git fetch origin --prune + + echo "๐ŸŒฟ Checking out branch: ${{ steps.branch-info.outputs.BRANCH_NAME }}" + git checkout ${{ steps.branch-info.outputs.BRANCH_NAME }} + git reset --hard origin/${{ steps.branch-info.outputs.BRANCH_NAME }} + + # Verify we're in the right directory + cd "$BACKEND_DIR" + + + # Environment setup + echo "โš™๏ธ Setting up environment..." + if [ ! -f ".env" ] && [ -f ".env.example" ]; then + echo "๐Ÿ“ Creating .env from .env.example..." + cp .env.example .env + echo "โš ๏ธ WARNING: Please update .env with production values!" + fi + + + # Clean up old containers and images + echo "๐Ÿงน Cleaning up old containers and images..." + sudo docker system prune -f || true + + # Build and deploy + echo "๐Ÿ—๏ธ Building and starting backend services..." + echo "๐Ÿ“ Using command: sudo docker compose down && sudo docker compose up --build -d" + + # Execute the exact command from AGENTS.md + sudo docker compose down && sudo docker compose up --build -d + + # Wait for services to be ready + echo "โณ Waiting for services to start..." + sleep 10 + + + # Deployment summary + echo "" + echo "๐ŸŽ‰ Backend deployment completed successfully!" + echo "๐Ÿ“… Completed at: $(date)" + echo "๐ŸŒฟ Branch: ${{ steps.branch-info.outputs.BRANCH_NAME }}" + echo "๐Ÿ“ Commit: ${{ steps.branch-info.outputs.COMMIT_SHORT }}" + echo "๐ŸŽฏ Environment: ${{ github.event.inputs.environment }}" + echo "" + echo "๐Ÿ“Š Service Status:" + sudo docker compose ps + echo "" + echo "๐ŸŒ Server should be available at:" + echo "- Nakama API: http://$(curl -s ifconfig.me):7350" + echo "- Health check: http://localhost:7350/v2" + + - name: Add deployment summary + run: | + echo "## ๐Ÿš€ Backend Deployment Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** ${{ steps.branch-info.outputs.BRANCH_NAME }}" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** ${{ steps.branch-info.outputs.COMMIT_SHORT }}" >> $GITHUB_STEP_SUMMARY + echo "**Environment:** ${{ github.event.inputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "**Deployed to:** GCP VM (${{ secrets.GCP_VM_HOST }})" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Deployment Process" >> $GITHUB_STEP_SUMMARY + echo "1. โœ… Repository updated and checkout completed" >> $GITHUB_STEP_SUMMARY + echo "2. โœ… Backend services stopped and rebuilt" >> $GITHUB_STEP_SUMMARY + echo "3. โœ… sudo docker containers started successfully" >> $GITHUB_STEP_SUMMARY + echo "4. โœ… Health checks passed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**๐ŸŽฏ Backend is now live and serving requests!**" >> $GITHUB_STEP_SUMMARY + + - name: Notify on failure + if: failure() + run: | + echo "## โŒ Backend Deployment Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** ${{ steps.branch-info.outputs.BRANCH_NAME }}" >> $GITHUB_STEP_SUMMARY + echo "**Commit:** ${{ steps.branch-info.outputs.COMMIT_SHORT }}" >> $GITHUB_STEP_SUMMARY + echo "**Environment:** ${{ github.event.inputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "โš ๏ธ Please check the workflow logs for details and consider:" >> $GITHUB_STEP_SUMMARY + echo "- Verifying GCP VM access and permissions" >> $GITHUB_STEP_SUMMARY + echo "- Checking sudo docker and sudo docker compose installation" >> $GITHUB_STEP_SUMMARY + echo "- Reviewing application logs on the server" >> $GITHUB_STEP_SUMMARY + echo "- Restoring from backup if necessary" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/deploy-frontend.yml b/.github/workflows/deploy-frontend.yml new file mode 100644 index 0000000..4159ce5 --- /dev/null +++ b/.github/workflows/deploy-frontend.yml @@ -0,0 +1,97 @@ +name: Frontend Production Deployment + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to deploy' + required: true + default: 'main' + type: string + environment: + description: 'Deployment environment' + required: true + default: 'production' + type: choice + options: + - production + - preview + +jobs: + deploy: + runs-on: ubuntu-latest + name: Deploy React Native Web App to Cloudflare Pages + permissions: + contents: read + deployments: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Get pnpm store directory + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Cache pnpm store + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Lint frontend + run: pnpm --filter frontend lint + + - name: Run frontend tests + run: pnpm --filter frontend test + + - name: Type check frontend + run: pnpm --filter frontend typecheck + + - name: Set environment variables + run: | + echo "EXPO_PUBLIC_NAKAMA_SERVER_HOST=${{ secrets.NAKAMA_SERVER_HOST || '127.0.0.1' }}" >> $GITHUB_ENV + echo "EXPO_PUBLIC_NAKAMA_SERVER_PORT=${{ secrets.NAKAMA_SERVER_PORT || '7350' }}" >> $GITHUB_ENV + echo "EXPO_PUBLIC_NAKAMA_SERVER_KEY=${{ secrets.NAKAMA_SERVER_KEY || 'defaultkey' }}" >> $GITHUB_ENV + echo "EXPO_PUBLIC_NAKAMA_USE_SSL=${{ secrets.NAKAMA_USE_SSL || 'false' }}" >> $GITHUB_ENV + + - name: Build React Native Web (Production) + working-directory: frontend + run: pnpm run build:web:prod + env: + NODE_ENV: production + + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy dist --project-name=tictactoe-game --compatibility-date=2024-09-29 + workingDirectory: frontend + env: + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + + - name: Add deployment summary + run: | + echo "## ๐Ÿš€ Deployment Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** ${{ github.event.inputs.branch }}" >> $GITHUB_STEP_SUMMARY + echo "**Environment:** ${{ github.event.inputs.environment }}" >> $GITHUB_STEP_SUMMARY + echo "**Deployed to:** Cloudflare Pages" >> $GITHUB_STEP_SUMMARY + echo "**Project:** tictactoe-game" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "โœ… Frontend successfully built and deployed!" >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index e7d3ebd..cdb50a4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ logs *.aab android/ ios/ +dist/ # Go builds backend/build/ @@ -41,3 +42,4 @@ coverage/ .idea/ .vscode/ .turbo/ +.ssh/ diff --git a/BACKEND_DEPLOYMENT.md b/BACKEND_DEPLOYMENT.md new file mode 100644 index 0000000..0f6a49a --- /dev/null +++ b/BACKEND_DEPLOYMENT.md @@ -0,0 +1,339 @@ +# Backend Deployment Guide + +## Overview + +This guide covers the automated deployment of the TicTacToe game backend to a GCP VM using GitHub Actions. + +## Prerequisites + +### 1. GCP VM Setup + +Before running the deployment, ensure your GCP VM is properly configured: + +```bash +# On your GCP VM, run the server setup script +curl -fsSL https://raw.githubusercontent.com/rogdex24/tictactoe-game/main/scripts/server-deploy-setup.sh | bash + +# Or download and run manually: +wget https://raw.githubusercontent.com/rogdex24/tictactoe-game/main/scripts/server-deploy-setup.sh +chmod +x server-deploy-setup.sh +./server-deploy-setup.sh +``` + +### 2. Required GitHub Secrets + +Configure these secrets in your GitHub repository settings (`Settings` > `Secrets and variables` > `Actions`): + +#### GCP VM Access + +- `GCP_VM_HOST`: IP address or hostname of your GCP VM +- `GCP_VM_SSH_USER`: SSH username (usually your GCP username) +- `GCP_VM_SSH_KEY`: Private SSH key for accessing the VM +- `GCP_VM_SSH_PORT`: SSH port (optional, defaults to 22) + +#### Example SSH Key Setup + +```bash +# On your local machine, generate SSH keys (if not already done) +ssh-keygen -t ed25519 -C "github-actions@yourdomain.com" + +# Copy the private key content for GitHub secret +cat ~/.ssh/id_ed25519 + +# Add public key to GCP VM +ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-vm-ip +``` + +## Deployment Workflow + +### Manual Deployment + +The backend deployment is triggered manually through GitHub Actions: + +1. **Navigate to Actions**: + - Go to your GitHub repository + - Click on the `Actions` tab + - Select `Backend Production Deployment` + +2. **Configure Deployment**: + - **Branch**: Choose which branch to deploy (default: main) + - **Environment**: Select production or staging + - **Rebuild Dependencies**: Check if you want to rebuild Go modules + +3. **Execute Deployment**: + - Click `Run workflow` + - Monitor the deployment progress in real-time + +### Deployment Process + +The workflow performs the following steps: + +1. **Pre-deployment**: + - Validates deployment parameters + - Connects to GCP VM via SSH + - Creates backup of current state + +2. **Code Update**: + - Fetches latest changes from GitHub + - Checks out the specified branch + - Updates local repository + +3. **Service Management**: + - Stops current backend services + - Rebuilds Go dependencies (if requested) + - Validates required files and environment + +4. **Docker Deployment**: + - Executes: `docker compose down && docker compose up --build -d` + - Builds fresh Docker images + - Starts services in detached mode + +5. **Health Checks**: + - Verifies containers are running + - Checks Nakama server responsiveness + - Retries with backoff on failure + +6. **Post-deployment**: + - Reports deployment status + - Provides service URLs and status + - Creates deployment summary + +## Backend Architecture + +### Docker Services + +The backend runs the following services: + +```yaml +# docker-compose.yml structure +services: + postgres: + image: postgres:13 + # Database for Nakama server state + + nakama: + build: . + # Game server with custom Go plugin + ports: + - '7350:7350' # HTTP API and WebSocket +``` + +### Environment Configuration + +Required environment variables in `.env`: + +```bash +# Database Configuration +POSTGRES_DB=nakama +POSTGRES_USER=nakama +POSTGRES_PASSWORD=your_secure_password + +# Nakama Configuration +NAKAMA_SERVER_KEY=your_server_key +NAKAMA_DB_PASSWORD=your_secure_password + +# Network Configuration +NAKAMA_PORT=7350 +POSTGRES_PORT=5432 +``` + +## Server Architecture + +### Go Plugin System + +The backend uses Nakama with a custom Go plugin: + +- **Location**: `backend/main.go`, `backend/match.go` +- **Features**: Server-authoritative matches, multiple game modes, leaderboards +- **Build Process**: Compiled into plugin during Docker build + +### Key Components + +1. **Matchmaking**: Custom callback for different game modes +2. **Game Logic**: Server-side validation and anti-cheat +3. **Leaderboard**: Global ranking system with statistics +4. **Real-time Communication**: WebSocket-based match updates + +## Troubleshooting + +### Common Issues + +1. **SSH Connection Failed**: + + ```bash + # Test SSH connection manually + ssh -i ~/.ssh/your-key user@vm-ip + + # Check SSH key permissions + chmod 600 ~/.ssh/your-key + ``` + +2. **Docker Build Failed**: + + ```bash + # Check Docker daemon status + sudo systemctl status docker + + # Check disk space + df -h + + # Clean up Docker resources + docker system prune -f + ``` + +3. **Go Build Issues**: + + ```bash + # Rebuild dependencies manually + cd ~/tictactoe-game/backend + go mod tidy + go mod vendor + ``` + +4. **Service Health Check Failed**: + + ```bash + # Check container status + docker compose ps + + # View logs + docker compose logs nakama + docker compose logs postgres + + # Check port availability + netstat -tlnp | grep 7350 + ``` + +### Manual Deployment Commands + +If automated deployment fails, you can deploy manually: + +```bash +# SSH into your GCP VM +ssh user@your-vm-ip + +# Navigate to application directory +cd ~/tictactoe-game/backend + +# Update code +git fetch origin +git checkout main +git reset --hard origin/main + +# Rebuild and restart services +docker compose down +docker compose up --build -d + +# Check status +docker compose ps +curl http://localhost:7350/v2 +``` + +### Rollback Procedure + +If deployment fails, rollback using the automatic backup: + +```bash +# Find backup location (shown in deployment logs) +ls /tmp/tictactoe-backup-* + +# Stop current services +cd ~/tictactoe-game/backend +docker compose down + +# Restore from backup +BACKUP_DIR="/tmp/tictactoe-backup-YYYYMMDD-HHMMSS" +cp -r "$BACKUP_DIR/backend"/* . + +# Restart services +docker compose up -d +``` + +## Monitoring and Maintenance + +### Service Monitoring + +```bash +# Check service status +docker compose ps + +# View real-time logs +docker compose logs -f + +# Monitor resource usage +docker stats + +# Check system resources +htop +df -h +``` + +### Database Backup + +```bash +# Create database backup +docker compose exec postgres pg_dump -U nakama nakama > backup_$(date +%Y%m%d).sql + +# Restore from backup +docker compose exec -T postgres psql -U nakama nakama < backup_file.sql +``` + +### Security Considerations + +1. **Firewall Rules**: Ensure only necessary ports are open +2. **SSL/TLS**: Configure HTTPS for production +3. **Database Security**: Use strong passwords and limit access +4. **SSH Keys**: Regularly rotate SSH keys +5. **Updates**: Keep system packages updated + +## Performance Optimization + +### Docker Optimization + +```bash +# Optimize Docker images +docker image prune -f + +# Monitor container performance +docker stats --no-stream + +# Limit container resources (in docker-compose.yml) +services: + nakama: + deploy: + resources: + limits: + memory: 512M + cpus: '0.5' +``` + +### Database Tuning + +```bash +# PostgreSQL performance tuning +# Edit postgresql.conf for production workloads +shared_buffers = 256MB +effective_cache_size = 1GB +max_connections = 100 +``` + +## API Endpoints + +After successful deployment, the following endpoints are available: + +- **Nakama API**: `http://your-vm-ip:7350/v2` +- **WebSocket**: `ws://your-vm-ip:7350/ws` +- **Health Check**: `http://your-vm-ip:7350/v2` +- **Admin Console**: `http://your-vm-ip:7351` (if enabled) + +## Next Steps + +1. **Configure Domain**: Set up DNS and reverse proxy +2. **SSL Certificate**: Install Let's Encrypt certificate +3. **Monitoring**: Set up application monitoring +4. **Backup Strategy**: Implement automated backups +5. **Scaling**: Configure horizontal scaling if needed + +--- + +**โš ๏ธ Important**: Always test deployments in a staging environment before deploying to production! diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..1b76f12 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,135 @@ +# Deployment Guide + +## Overview + +This repository includes automated deployment for both frontend and backend components: + +- **Frontend**: React Native Web app deployed to Cloudflare Pages +- **Backend**: Nakama game server deployed to GCP VM via Docker + +## Quick Links + +- ๐ŸŒ **[Frontend Deployment Guide](#frontend-deployment)** - React Native Web to Cloudflare Pages +- ๐Ÿš€ **[Backend Deployment Guide](./BACKEND_DEPLOYMENT.md)** - Nakama server to GCP VM +- ๐Ÿ”ง **[Server Setup Script](./scripts/server-deploy-setup.sh)** - Automated server provisioning + +--- + +## Frontend Deployment + +## Deployment Workflow + +The deployment is triggered manually through GitHub Actions and supports: + +- **Manual trigger**: Deploy any branch on demand +- **Branch selection**: Choose which branch to deploy +- **Environment selection**: Production or preview deployment + +## Required GitHub Secrets + +Configure these secrets in your GitHub repository settings (`Settings` > `Secrets and variables` > `Actions`): + +### Cloudflare Configuration + +- `CLOUDFLARE_API_TOKEN`: Your Cloudflare API token with Pages permissions +- `CLOUDFLARE_ACCOUNT_ID`: Your Cloudflare account ID + +### Nakama Server Configuration (Optional) + +If not provided, the deployment will use default localhost values: + +- `NAKAMA_SERVER_HOST`: Nakama server hostname (default: `127.0.0.1`) +- `NAKAMA_SERVER_PORT`: Nakama server port (default: `7350`) +- `NAKAMA_SERVER_KEY`: Nakama server key (default: `defaultkey`) +- `NAKAMA_USE_SSL`: Whether to use SSL for Nakama connection (default: `false`) + +## How to Deploy + +1. **Manual Deployment**: + - Go to the `Actions` tab in your GitHub repository + - Select `Frontend Production Deployment` + - Click `Run workflow` + - Choose: + - **Branch**: The branch you want to deploy + - **Environment**: `production` or `preview` + - Click `Run workflow` + +2. **Deployment Process**: + The workflow will: + - Checkout the specified branch + - Setup Node.js and pnpm + - Install dependencies + - Run linting and tests + - Build the React Native Web production bundle + - Deploy to Cloudflare Pages + +## Build Output + +- **Build command**: `pnpm run build:web:prod` +- **Build directory**: `frontend/dist` +- **Project name**: `tictactoe-game` + +## Cloudflare Pages Setup + +1. **Create Cloudflare Pages Project**: + - Login to Cloudflare Dashboard + - Go to `Pages` > `Create a project` + - Connect to Git or use Direct upload + - Set project name as `tictactoe-game` + +2. **Build Settings** (if using Git integration): + - Build command: `cd frontend && pnpm install && pnpm run build:web:prod` + - Build output directory: `frontend/dist` + - Root directory: `/` + +## Environment Variables + +The build process uses the following environment variables: + +```bash +NODE_ENV=production +EXPO_PUBLIC_NAKAMA_SERVER_HOST=${NAKAMA_SERVER_HOST} +EXPO_PUBLIC_NAKAMA_SERVER_PORT=${NAKAMA_SERVER_PORT} +EXPO_PUBLIC_NAKAMA_SERVER_KEY=${NAKAMA_SERVER_KEY} +EXPO_PUBLIC_NAKAMA_USE_SSL=${NAKAMA_USE_SSL} +``` + +## Troubleshooting + +### Common Issues + +1. **Build Fails**: Check that all dependencies are properly installed and the build script works locally +2. **Cloudflare Deploy Fails**: Verify API token permissions and account ID +3. **App Doesn't Connect to Backend**: Check Nakama server environment variables + +### Local Testing + +To test the production build locally: + +```bash +cd frontend +pnpm run build:web:prod +npx serve dist +``` + +The app will be available at `http://localhost:3000`. + +## Custom Domain + +After deployment, you can: + +1. Go to your Cloudflare Pages project +2. Navigate to `Custom domains` +3. Add your custom domain +4. Configure DNS settings as instructed + +## Performance + +The production build includes: + +- **Tree shaking**: Removes unused code +- **Minification**: Reduces bundle size +- **Asset optimization**: Optimizes fonts and images +- **Web platform optimizations**: React Native Web specific optimizations + +Current bundle size: ~1.1MB (minified) diff --git a/backend/.golangci.yml b/backend/.golangci.yml index 59c06a7..9d33ea7 100644 --- a/backend/.golangci.yml +++ b/backend/.golangci.yml @@ -1,4 +1,4 @@ -version: 2 +version: '2' run: timeout: 5m @@ -7,10 +7,12 @@ run: issues: max-issues-per-linter: 50 max-same-issues: 10 - exclude-dirs: - - vendor - - node_modules - - .git - exclude-files: - - ".*\\.pb\\.go$" - - ".*_generated\\.go$" + +linters: + exclusions: + paths: + - vendor/ + - node_modules/ + - .git/ + - ".*\\.pb\\.go$" + - ".*_generated\\.go$" diff --git a/frontend/app.json b/frontend/app.json index 07062c1..14f985d 100644 --- a/frontend/app.json +++ b/frontend/app.json @@ -15,7 +15,7 @@ "updates": { "fallbackToCacheTimeout": 0 }, - "assetBundlePatterns": ["**/*"], + "assetBundlePatterns": [], "ios": { "supportsTablet": true }, @@ -26,7 +26,21 @@ } }, "web": { - "bundler": "metro" + "bundler": "metro", + "name": "TicTacToe Game", + "shortName": "TicTacToe", + "lang": "en", + "scope": "/", + "themeColor": "#000000", + "backgroundColor": "#ffffff", + "display": "standalone", + "orientation": "portrait", + "startUrl": "/", + "build": { + "babel": { + "include": ["@react-native/polyfills"] + } + } } } } diff --git a/frontend/assets/adaptive-icon.png b/frontend/assets/adaptive-icon.png index ee9aa23..4950301 100644 Binary files a/frontend/assets/adaptive-icon.png and b/frontend/assets/adaptive-icon.png differ diff --git a/frontend/assets/icon.png b/frontend/assets/icon.png index ee9aa23..4950301 100644 Binary files a/frontend/assets/icon.png and b/frontend/assets/icon.png differ diff --git a/frontend/assets/splash.png b/frontend/assets/splash.png index ee9aa23..4950301 100644 Binary files a/frontend/assets/splash.png and b/frontend/assets/splash.png differ diff --git a/frontend/index.web.ts b/frontend/index.web.ts new file mode 100644 index 0000000..6a08997 --- /dev/null +++ b/frontend/index.web.ts @@ -0,0 +1,44 @@ +import { registerRootComponent } from 'expo'; +import { Platform } from 'react-native'; + +import App from './App'; + +// Web-specific polyfills and setup +if (Platform.OS === 'web') { + // Ensure proper meta viewport tag for responsive design + if (typeof document !== 'undefined') { + const viewport = document.querySelector('meta[name="viewport"]'); + if (!viewport) { + const meta = document.createElement('meta'); + meta.name = 'viewport'; + meta.content = 'width=device-width, initial-scale=1, shrink-to-fit=no'; + document.head.appendChild(meta); + } + + // Add touch-action CSS for better touch handling + const style = document.createElement('style'); + style.textContent = ` + body { + touch-action: pan-x pan-y; + -webkit-touch-callout: none; + -webkit-user-select: none; + user-select: none; + overscroll-behavior: none; + } + + /* Prevent unwanted scrolling on game board */ + .game-board { + touch-action: none; + } + + /* Ensure proper font rendering */ + * { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + `; + document.head.appendChild(style); + } +} + +registerRootComponent(App); diff --git a/frontend/metro.config.js b/frontend/metro.config.js new file mode 100644 index 0000000..3c639b3 --- /dev/null +++ b/frontend/metro.config.js @@ -0,0 +1,79 @@ +const { getDefaultConfig } = require('expo/metro-config'); + +/** @type {import('expo/metro-config').MetroConfig} */ +const config = getDefaultConfig(__dirname, { + // Enable CSS support for web + isCSSEnabled: true, +}); + +// Configure web platform support +config.resolver.platforms = ['ios', 'android', 'native', 'web']; + +// Extend asset extensions for better web support +config.resolver.assetExts.push( + // Fonts + 'ttf', + 'otf', + 'woff', + 'woff2', + 'eot', + // Images + 'svg', + 'gif', + 'webp', + 'ico', + // Audio/Video + 'mp3', + 'mp4', + 'mov', + 'avi', + 'webm', + // Documents + 'pdf', +); + +// Configure source extensions for web +config.resolver.sourceExts = [ + ...config.resolver.sourceExts, + 'web.js', + 'web.jsx', + 'web.ts', + 'web.tsx', +]; + +// Configure module resolution for web +config.resolver.alias = { + 'react-native$': 'react-native-web', + 'react-native/Libraries/Utilities/Platform': 'react-native-web/dist/exports/Platform', + 'react-native/Libraries/Image/AssetRegistry': 'react-native-web/dist/modules/AssetRegistry', + 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter': + 'react-native-web/dist/vendor/react-native/NativeEventEmitter', +}; + +// Configure SVG transformer +config.resolver.assetExts = config.resolver.assetExts.filter((ext) => ext !== 'svg'); +config.resolver.sourceExts.push('svg'); + +// Configure transformer for different file types +config.transformer = { + ...config.transformer, + unstable_allowRequireContext: true, + assetPlugins: ['expo-asset/tools/hashAssetFiles'], + babelTransformerPath: require.resolve('react-native-svg-transformer'), +}; + +// Ignore unnecessary files for web builds +config.resolver.blockList = [/.*\.native\.js$/, /.*\.ios\.js$/, /.*\.android\.js$/]; + +// Configure web-specific optimizations +if (process.env.NODE_ENV === 'production') { + // Enable tree shaking for production builds + config.transformer.minifierConfig = { + keep_fnames: true, + mangle: { + keep_fnames: true, + }, + }; +} + +module.exports = config; diff --git a/frontend/package.json b/frontend/package.json index 0a3bff7..5834fca 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,14 +6,17 @@ "scripts": { "start": "expo start", "start:prod": "NODE_ENV=production expo start", + "start:web": "expo start --web", + "build:web": "expo export --platform web", + "build:web:prod": "NODE_ENV=production expo export --platform web", + "serve:web": "npx serve dist", "lint": "eslint . --ext .js,.jsx,.ts,.tsx", "format": "prettier --check .", "format:write": "prettier --write .", "test": "jest --passWithNoTests", "test:logger": "node scripts/logger-demo.js", "test:logger:prod": "NODE_ENV=production node scripts/logger-demo.js", - "typecheck": "tsc --noEmit", - "build": "node scripts/build-message.cjs" + "typecheck": "tsc --noEmit" }, "dependencies": { "@heroiclabs/nakama-js": "^2.8.0", @@ -31,7 +34,8 @@ "react-native-safe-area-context": "~5.6.0", "react-native-screens": "^4.3.0", "react-native-svg": "15.12.1", - "react-native-web": "^0.21.0" + "react-native-web": "^0.21.0", + "react-native-svg-transformer": "^1.5.0" }, "devDependencies": { "@testing-library/jest-native": "^5.4.3", @@ -47,6 +51,7 @@ "jest-expo": "^54.0.0", "prettier": "^3.2.5", "react-test-renderer": "19.1.0", + "serve": "^14.2.1", "typescript": "~5.9.2" } } diff --git a/frontend/scripts/build-message.cjs b/frontend/scripts/build-message.cjs deleted file mode 100755 index e41666b..0000000 --- a/frontend/scripts/build-message.cjs +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -console.log('Expo build handled via EAS or CI.'); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bf9fef..b9af8d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,6 +96,9 @@ importers: react-native-svg: specifier: 15.12.1 version: 15.12.1(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0) + react-native-svg-transformer: + specifier: ^1.5.0 + version: 1.5.1(react-native-svg@15.12.1(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(typescript@5.9.2) react-native-web: specifier: ^0.21.0 version: 0.21.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -139,6 +142,9 @@ importers: react-test-renderer: specifier: 19.1.0 version: 19.1.0(react@19.1.0) + serve: + specifier: ^14.2.1 + version: 14.2.5 typescript: specifier: ~5.9.2 version: 5.9.2 @@ -1817,6 +1823,119 @@ packages: integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==, } + '@svgr/babel-plugin-add-jsx-attribute@8.0.0': + resolution: + { + integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: + { + integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: + { + integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0': + resolution: + { + integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0': + resolution: + { + integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0': + resolution: + { + integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0': + resolution: + { + integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@8.0.0': + resolution: + { + integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==, + } + engines: { node: '>=12' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@8.1.0': + resolution: + { + integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==, + } + engines: { node: '>=14' } + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@8.1.0': + resolution: + { + integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==, + } + engines: { node: '>=14' } + + '@svgr/hast-util-to-babel-ast@8.0.0': + resolution: + { + integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==, + } + engines: { node: '>=14' } + + '@svgr/plugin-jsx@8.1.0': + resolution: + { + integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==, + } + engines: { node: '>=14' } + peerDependencies: + '@svgr/core': '*' + + '@svgr/plugin-svgo@8.1.0': + resolution: + { + integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==, + } + engines: { node: '>=14' } + peerDependencies: + '@svgr/core': '*' + '@testing-library/jest-native@5.4.3': resolution: { @@ -1853,6 +1972,13 @@ packages: } engines: { node: '>= 10' } + '@trysound/sax@0.2.0': + resolution: + { + integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==, + } + engines: { node: '>=10.13.0' } + '@types/babel__core@7.20.5': resolution: { @@ -2208,6 +2334,12 @@ packages: integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==, } + '@zeit/schemas@2.36.0': + resolution: + { + integrity: sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==, + } + JSONStream@1.3.5: resolution: { @@ -2320,6 +2452,12 @@ packages: integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, } + ajv@8.12.0: + resolution: + { + integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==, + } + ajv@8.17.1: resolution: { @@ -2332,6 +2470,12 @@ packages: integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==, } + ansi-align@3.0.1: + resolution: + { + integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==, + } + ansi-escapes@4.3.2: resolution: { @@ -2427,6 +2571,12 @@ packages: integrity: sha512-Quji6+8kLBC3NnBeo14nPDq0+2jUs5s3/xEye+udFHumHhRk4M7aAMXp/PBJqkKYGuuyR9M/6Dq7d2AViiGmhw==, } + arch@2.2.0: + resolution: + { + integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==, + } + arg@5.0.2: resolution: { @@ -2714,6 +2864,13 @@ packages: integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, } + boxen@7.0.0: + resolution: + { + integrity: sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==, + } + engines: { node: '>=14.16' } + bplist-creator@0.1.0: resolution: { @@ -2779,6 +2936,13 @@ packages: integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==, } + bytes@3.0.0: + resolution: + { + integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==, + } + engines: { node: '>= 0.8' } + bytes@3.1.2: resolution: { @@ -2849,12 +3013,26 @@ packages: } engines: { node: '>=10' } + camelcase@7.0.1: + resolution: + { + integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==, + } + engines: { node: '>=14.16' } + caniuse-lite@1.0.30001745: resolution: { integrity: sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==, } + chalk-template@0.4.0: + resolution: + { + integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==, + } + engines: { node: '>=12' } + chalk@2.4.2: resolution: { @@ -2876,6 +3054,13 @@ packages: } engines: { node: '>=10' } + chalk@5.0.1: + resolution: + { + integrity: sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==, + } + engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } + chalk@5.6.2: resolution: { @@ -2944,6 +3129,13 @@ packages: integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==, } + cli-boxes@3.0.0: + resolution: + { + integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==, + } + engines: { node: '>=10' } + cli-cursor@2.1.0: resolution: { @@ -2979,6 +3171,13 @@ packages: } engines: { node: '>=18' } + clipboardy@3.0.0: + resolution: + { + integrity: sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + cliui@6.0.0: resolution: { @@ -3149,6 +3348,13 @@ packages: } engines: { node: '>= 0.10.0' } + content-disposition@0.5.2: + resolution: + { + integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==, + } + engines: { node: '>= 0.6' } + conventional-changelog-angular@7.0.0: resolution: { @@ -3201,6 +3407,18 @@ packages: } engines: { node: '>=4' } + cosmiconfig@8.3.6: + resolution: + { + integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==, + } + engines: { node: '>=14' } + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cosmiconfig@9.0.0: resolution: { @@ -3260,6 +3478,20 @@ packages: } engines: { node: '>=8.0.0' } + css-tree@2.2.1: + resolution: + { + integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==, + } + engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0' } + + css-tree@2.3.1: + resolution: + { + integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==, + } + engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } + css-what@6.2.2: resolution: { @@ -3267,6 +3499,13 @@ packages: } engines: { node: '>= 6' } + csso@5.0.5: + resolution: + { + integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==, + } + engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0' } + cssom@0.3.8: resolution: { @@ -3541,6 +3780,12 @@ packages: integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==, } + dot-case@3.0.4: + resolution: + { + integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==, + } + dot-prop@5.3.0: resolution: { @@ -4989,6 +5234,13 @@ packages: } engines: { node: '>=8' } + is-port-reachable@4.0.0: + resolution: + { + integrity: sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + is-potential-custom-element-name@1.0.1: resolution: { @@ -5860,6 +6112,12 @@ packages: } hasBin: true + lower-case@2.0.2: + resolution: + { + integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==, + } + lru-cache@10.4.3: resolution: { @@ -5904,6 +6162,18 @@ packages: integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==, } + mdn-data@2.0.28: + resolution: + { + integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==, + } + + mdn-data@2.0.30: + resolution: + { + integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==, + } + memoize-one@5.2.1: resolution: { @@ -6143,6 +6413,13 @@ packages: } engines: { node: '>=8.6' } + mime-db@1.33.0: + resolution: + { + integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==, + } + engines: { node: '>= 0.6' } + mime-db@1.52.0: resolution: { @@ -6157,6 +6434,13 @@ packages: } engines: { node: '>= 0.6' } + mime-types@2.1.18: + resolution: + { + integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==, + } + engines: { node: '>= 0.6' } + mime-types@2.1.35: resolution: { @@ -6321,6 +6605,12 @@ packages: integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==, } + no-case@3.0.4: + resolution: + { + integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==, + } + nocache@3.0.4: resolution: { @@ -6682,6 +6972,12 @@ packages: } engines: { node: '>= 0.8' } + path-dirname@1.0.2: + resolution: + { + integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==, + } + path-exists@4.0.0: resolution: { @@ -6703,6 +6999,12 @@ packages: } engines: { node: '>=0.10.0' } + path-is-inside@1.0.2: + resolution: + { + integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==, + } + path-key@3.1.1: resolution: { @@ -6730,6 +7032,12 @@ packages: } engines: { node: '>=16 || 14 >=14.18' } + path-to-regexp@3.3.0: + resolution: + { + integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==, + } + path-type@4.0.0: resolution: { @@ -6945,6 +7253,13 @@ packages: integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==, } + range-parser@1.2.0: + resolution: + { + integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==, + } + engines: { node: '>= 0.6' } + range-parser@1.2.1: resolution: { @@ -7033,6 +7348,15 @@ packages: react: '*' react-native: '*' + react-native-svg-transformer@1.5.1: + resolution: + { + integrity: sha512-dFvBNR8A9VPum9KCfh+LE49YiJEF8zUSnEFciKQroR/bEOhlPoZA0SuQ0qNk7m2iZl2w59FYjdRe0pMHWMDl0Q==, + } + peerDependencies: + react-native: '>=0.59.0' + react-native-svg: '>=12.0.0' + react-native-svg@15.12.1: resolution: { @@ -7152,6 +7476,19 @@ packages: } engines: { node: '>=4' } + registry-auth-token@3.3.2: + resolution: + { + integrity: sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==, + } + + registry-url@3.1.0: + resolution: + { + integrity: sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==, + } + engines: { node: '>=0.10.0' } + regjsgen@0.8.0: resolution: { @@ -7416,6 +7753,12 @@ packages: integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==, } + serve-handler@6.1.6: + resolution: + { + integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==, + } + serve-static@1.16.2: resolution: { @@ -7423,6 +7766,14 @@ packages: } engines: { node: '>= 0.8.0' } + serve@14.2.5: + resolution: + { + integrity: sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==, + } + engines: { node: '>= 14' } + hasBin: true + server-only@0.0.1: resolution: { @@ -7590,6 +7941,12 @@ packages: } engines: { node: '>=8.0.0' } + snake-case@3.0.4: + resolution: + { + integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==, + } + source-map-js@1.2.1: resolution: { @@ -7950,6 +8307,20 @@ packages: } engines: { node: '>= 0.4' } + svg-parser@2.0.4: + resolution: + { + integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==, + } + + svgo@3.3.2: + resolution: + { + integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==, + } + engines: { node: '>=14.0.0' } + hasBin: true + symbol-tree@3.2.4: resolution: { @@ -8123,6 +8494,12 @@ packages: integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==, } + tslib@2.8.1: + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, + } + turbo-darwin-64@1.13.4: resolution: { @@ -8213,6 +8590,13 @@ packages: } engines: { node: '>=8' } + type-fest@2.19.0: + resolution: + { + integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==, + } + engines: { node: '>=12.20' } + typed-array-buffer@1.0.3: resolution: { @@ -8348,6 +8732,12 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + update-check@1.5.4: + resolution: + { + integrity: sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==, + } + uri-js@4.4.1: resolution: { @@ -8577,6 +8967,13 @@ packages: engines: { node: '>= 8' } hasBin: true + widest-line@4.0.1: + resolution: + { + integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==, + } + engines: { node: '>=12' } + wonka@6.3.5: resolution: { @@ -10470,6 +10867,85 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + + '@svgr/babel-preset@8.1.0(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.4) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.4) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.4) + + '@svgr/core@8.1.0(typescript@5.9.2)': + dependencies: + '@babel/core': 7.28.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4) + camelcase: 6.3.0 + cosmiconfig: 8.3.6(typescript@5.9.2) + snake-case: 3.0.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@svgr/hast-util-to-babel-ast@8.0.0': + dependencies: + '@babel/types': 7.28.4 + entities: 4.5.0 + + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))': + dependencies: + '@babel/core': 7.28.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4) + '@svgr/core': 8.1.0(typescript@5.9.2) + '@svgr/hast-util-to-babel-ast': 8.0.0 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)': + dependencies: + '@svgr/core': 8.1.0(typescript@5.9.2) + cosmiconfig: 8.3.6(typescript@5.9.2) + deepmerge: 4.3.1 + svgo: 3.3.2 + transitivePeerDependencies: + - typescript + '@testing-library/jest-native@5.4.3(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: chalk: 4.1.2 @@ -10494,6 +10970,8 @@ snapshots: '@tootallnate/once@2.0.0': {} + '@trysound/sax@0.2.0': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.4 @@ -10767,6 +11245,8 @@ snapshots: '@xtuc/long@4.2.2': {} + '@zeit/schemas@2.36.0': {} + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -10830,6 +11310,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.12.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -10839,6 +11326,10 @@ snapshots: anser@1.4.10: {} + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -10884,6 +11375,8 @@ snapshots: appdirsjs@1.2.7: optional: true + arch@2.2.0: {} + arg@5.0.2: {} argparse@1.0.10: @@ -11130,6 +11623,17 @@ snapshots: boolbase@1.0.0: {} + boxen@7.0.0: + dependencies: + ansi-align: 3.0.1 + camelcase: 7.0.1 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 5.1.2 + type-fest: 2.19.0 + widest-line: 4.0.1 + wrap-ansi: 8.1.0 + bplist-creator@0.1.0: dependencies: stream-buffers: 2.2.0 @@ -11174,6 +11678,8 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + bytes@3.0.0: {} + bytes@3.1.2: {} call-bind-apply-helpers@1.0.2: @@ -11209,8 +11715,14 @@ snapshots: camelcase@6.3.0: {} + camelcase@7.0.1: {} + caniuse-lite@1.0.30001745: {} + chalk-template@0.4.0: + dependencies: + chalk: 4.1.2 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -11227,6 +11739,8 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.0.1: {} + chalk@5.6.2: {} char-regex@1.0.2: {} @@ -11263,6 +11777,8 @@ snapshots: cjs-module-lexer@1.4.3: {} + cli-boxes@3.0.0: {} + cli-cursor@2.1.0: dependencies: restore-cursor: 2.0.0 @@ -11283,6 +11799,12 @@ snapshots: slice-ansi: 5.0.0 string-width: 7.2.0 + clipboardy@3.0.0: + dependencies: + arch: 2.2.0 + execa: 5.1.1 + is-wsl: 2.2.0 + cliui@6.0.0: dependencies: string-width: 4.2.3 @@ -11381,6 +11903,8 @@ snapshots: transitivePeerDependencies: - supports-color + content-disposition@0.5.2: {} + conventional-changelog-angular@7.0.0: dependencies: compare-func: 2.0.0 @@ -11416,6 +11940,15 @@ snapshots: js-yaml: 3.14.1 parse-json: 4.0.0 + cosmiconfig@8.3.6(typescript@5.9.2): + dependencies: + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + optionalDependencies: + typescript: 5.9.2 + cosmiconfig@9.0.0(typescript@5.9.2): dependencies: env-paths: 2.2.1 @@ -11471,8 +12004,22 @@ snapshots: mdn-data: 2.0.14 source-map: 0.6.1 + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.1 + css-what@6.2.2: {} + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + cssom@0.3.8: {} cssom@0.5.0: {} @@ -11603,6 +12150,11 @@ snapshots: domelementtype: 2.3.0 domhandler: 5.0.3 + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -12586,6 +13138,8 @@ snapshots: is-path-inside@3.0.3: {} + is-port-reachable@4.0.0: {} + is-potential-custom-element-name@1.0.1: {} is-regex@1.2.1: @@ -13338,6 +13892,10 @@ snapshots: dependencies: js-tokens: 4.0.0 + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + lru-cache@10.4.3: {} lru-cache@5.1.1: @@ -13358,6 +13916,10 @@ snapshots: mdn-data@2.0.14: {} + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + memoize-one@5.2.1: {} memoize-one@6.0.0: {} @@ -13723,10 +14285,16 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.33.0: {} + mime-db@1.52.0: {} mime-db@1.54.0: {} + mime-types@2.1.18: + dependencies: + mime-db: 1.33.0 + mime-types@2.1.35: dependencies: mime-db: 1.52.0 @@ -13790,6 +14358,11 @@ snapshots: nested-error-stacks@2.0.1: {} + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + nocache@3.0.4: optional: true @@ -14018,12 +14591,16 @@ snapshots: parseurl@1.3.3: {} + path-dirname@1.0.2: {} + path-exists@4.0.0: {} path-exists@5.0.0: {} path-is-absolute@1.0.1: {} + path-is-inside@1.0.2: {} + path-key@3.1.1: {} path-key@4.0.0: {} @@ -14035,6 +14612,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@3.3.0: {} + path-type@4.0.0: {} picocolors@1.1.1: {} @@ -14141,6 +14720,8 @@ snapshots: dependencies: safe-buffer: 5.2.1 + range-parser@1.2.0: {} + range-parser@1.2.1: {} rc@1.2.8: @@ -14194,6 +14775,18 @@ snapshots: react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0) warn-once: 0.1.1 + react-native-svg-transformer@1.5.1(react-native-svg@15.12.1(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(typescript@5.9.2): + dependencies: + '@svgr/core': 8.1.0(typescript@5.9.2) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2) + path-dirname: 1.0.2 + react-native: 0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0) + react-native-svg: 15.12.1(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0) + transitivePeerDependencies: + - supports-color + - typescript + react-native-svg@15.12.1(react-native@0.81.4(@babel/core@7.28.4)(@react-native-community/cli@12.3.7)(@types/react@19.1.13)(react@19.1.0))(react@19.1.0): dependencies: css-select: 5.2.2 @@ -14332,6 +14925,15 @@ snapshots: unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.1 + registry-auth-token@3.3.2: + dependencies: + rc: 1.2.8 + safe-buffer: 5.2.1 + + registry-url@3.1.0: + dependencies: + rc: 1.2.8 + regjsgen@0.8.0: {} regjsparser@0.13.0: @@ -14499,6 +15101,16 @@ snapshots: dependencies: randombytes: 2.1.0 + serve-handler@6.1.6: + dependencies: + bytes: 3.0.0 + content-disposition: 0.5.2 + mime-types: 2.1.18 + minimatch: 3.1.2 + path-is-inside: 1.0.2 + path-to-regexp: 3.3.0 + range-parser: 1.2.0 + serve-static@1.16.2: dependencies: encodeurl: 2.0.0 @@ -14508,6 +15120,22 @@ snapshots: transitivePeerDependencies: - supports-color + serve@14.2.5: + dependencies: + '@zeit/schemas': 2.36.0 + ajv: 8.12.0 + arg: 5.0.2 + boxen: 7.0.0 + chalk: 5.0.1 + chalk-template: 0.4.0 + clipboardy: 3.0.0 + compression: 1.8.1 + is-port-reachable: 4.0.0 + serve-handler: 6.1.6 + update-check: 1.5.4 + transitivePeerDependencies: + - supports-color + server-only@0.0.1: {} set-blocking@2.0.0: @@ -14614,6 +15242,11 @@ snapshots: slugify@1.6.6: {} + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + source-map-js@1.2.1: {} source-map-support@0.5.13: @@ -14825,6 +15458,18 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svg-parser@2.0.4: {} + + svgo@3.3.2: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 5.2.2 + css-tree: 2.3.1 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + symbol-tree@3.2.4: {} tapable@2.2.3: {} @@ -14918,6 +15563,8 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tslib@2.8.1: {} + turbo-darwin-64@1.13.4: optional: true @@ -14957,6 +15604,8 @@ snapshots: type-fest@0.7.1: {} + type-fest@2.19.0: {} + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -15035,6 +15684,11 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 + update-check@1.5.4: + dependencies: + registry-auth-token: 3.3.2 + registry-url: 3.1.0 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -15202,6 +15856,10 @@ snapshots: dependencies: isexe: 2.0.0 + widest-line@4.0.1: + dependencies: + string-width: 5.1.2 + wonka@6.3.5: {} word-wrap@1.2.5: {} diff --git a/scripts/gcp-vm-setup.sh b/scripts/gcp-vm-setup.sh new file mode 100644 index 0000000..f4e3e3b --- /dev/null +++ b/scripts/gcp-vm-setup.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Download nano text editor ( Ubuntu Minimal does not come with a text editor) +sudo snap install nano --classic + +# Make the setup script +nano setup_script.sh +# Paste the following content of server-deploy-setup.sh and save the file + +# Give execute permission +chmod +x setup_script.sh + +# Run the setup script +./setup_script.sh + diff --git a/scripts/install-go-tools.sh b/scripts/install-go-tools.sh index 8862f4d..4f21204 100755 --- a/scripts/install-go-tools.sh +++ b/scripts/install-go-tools.sh @@ -7,11 +7,24 @@ export GOTOOLCHAIN="${GOTOOLCHAIN:-$GO_TOOLCHAIN_VERSION}" # pin exact tool versions to keep CI and local environments in sync GOIMPORTS_VERSION="v0.27.0" -GOLANGCI_LINT_VERSION="v1.64.0" printf 'Installing Go tools with toolchain %s\n' "$GOTOOLCHAIN" go install golang.org/x/tools/cmd/goimports@${GOIMPORTS_VERSION} -go install github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCI_LINT_VERSION} + +# Install golangci-lint +# docs: https://golangci-lint.run/docs/welcome/install/#local-installation + +if [[ "$(uname)" == "Darwin" ]]; then + if command -v brew >/dev/null 2>&1; then + brew install golangci-lint || brew upgrade golangci-lint + else + echo "Homebrew not found. Installing golangci-lint using go install instead." + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + fi +else + # Not macOS, use go install + go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest +fi echo "Go tooling installed in $(go env GOBIN || echo "$(go env GOPATH)/bin")" diff --git a/scripts/server-deploy-setup.sh b/scripts/server-deploy-setup.sh new file mode 100755 index 0000000..0492465 --- /dev/null +++ b/scripts/server-deploy-setup.sh @@ -0,0 +1,84 @@ +#!/bin/bash +set -euo pipefail + +# Server Deploy Setup Script +# This script installs Docker and sets up the server environment for deployment + +echo "๐Ÿš€ Starting server deployment setup..." + +# Check if running as root +if [[ $EUID -eq 0 ]]; then + echo "โŒ This script should not be run as root. Please run as a regular user with sudo privileges." + exit 1 +fi + +# Check for sudo privileges +if ! sudo -v >/dev/null 2>&1; then + echo "โŒ This script requires sudo privileges. Please make sure your user has sudo access." + exit 1 +fi + +echo "โœ… Running with proper user privileges" + +# Update system packages +echo "๐Ÿ“ฆ Updating system packages..." +sudo apt-get update -y +sudo apt-get upgrade -y + +# Install required packages +echo "๐Ÿ“ฆ Installing required packages..." +sudo apt-get install -y \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg \ + lsb-release \ + git \ + wget \ + unzip + +# Install Docker +echo "๐Ÿณ Installing Docker..." +if command -v docker >/dev/null 2>&1; then + echo "โ„น๏ธ Docker is already installed. Version: $(docker --version)" +else + echo "๐Ÿ“ฅ Downloading and installing Docker..." + sudo curl -fsSL https://get.docker.com | sh + + # Add user to docker group + echo "๐Ÿ‘ค Adding user to docker group..." + sudo usermod -aG docker $USER + + echo "โœ… Docker installed successfully" +fi + + +# Start and enable Docker service +echo "๐Ÿ”„ Starting Docker service..." +sudo systemctl start docker +sudo systemctl enable docker + + +# Download Repo +REPO_URL="https://github.com/rogdex24/tictactoe-game.git" +APP_DIR="$HOME/tictactoe-game" +if [ -d "$APP_DIR" ]; then + echo "โ„น๏ธ Application directory already exists. Pulling latest changes..." + cd "$APP_DIR" + git pull origin main +else + echo "๐Ÿ“ฅ Cloning application repository..." + git clone "$REPO_URL" "$APP_DIR" + cd "$APP_DIR" +fi + +# Create .env +cp .env.example .env +echo "โœ… Created .env file from .env.example. Please update it with your configuration." + + +# Display completion message +echo "" +echo "๐ŸŽ‰ Server deployment setup completed successfully!" +echo "" +echo "โœ… Setup complete! Your server is ready for deployment." \ No newline at end of file