Add image resize and crop tools #18
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy | |
| on: | |
| push: | |
| branches: | |
| - master | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: coding-tools-production | |
| cancel-in-progress: false | |
| env: | |
| NODE_VERSION: "24" | |
| jobs: | |
| build: | |
| name: Check and build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ env.NODE_VERSION }} | |
| cache: npm | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build static site | |
| run: npm run build | |
| deploy: | |
| name: Deploy to VPS | |
| needs: build | |
| runs-on: ubuntu-latest | |
| environment: production | |
| steps: | |
| - name: Validate required secrets | |
| env: | |
| VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }} | |
| run: | | |
| if [ -z "${VPS_SSH_KEY}" ]; then | |
| echo "::error::Missing required repository secret: VPS_SSH_KEY" | |
| exit 1 | |
| fi | |
| - name: Install SSH key | |
| env: | |
| VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }} | |
| run: | | |
| install -m 700 -d ~/.ssh | |
| printf '%s\n' "${VPS_SSH_KEY}" > ~/.ssh/deploy_key | |
| chmod 600 ~/.ssh/deploy_key | |
| - name: Trust VPS host key | |
| env: | |
| VPS_HOST: ${{ secrets.VPS_HOST || '35.239.243.94' }} | |
| VPS_PORT: ${{ secrets.VPS_PORT || '22' }} | |
| run: | | |
| mkdir -p ~/.ssh | |
| ssh-keyscan -p "${VPS_PORT}" -H "${VPS_HOST}" >> ~/.ssh/known_hosts | |
| - name: Pull, build, and reload nginx | |
| env: | |
| VPS_HOST: ${{ secrets.VPS_HOST || '35.239.243.94' }} | |
| VPS_USER: ${{ secrets.VPS_USER || 'bylearner' }} | |
| VPS_PORT: ${{ secrets.VPS_PORT || '22' }} | |
| VPS_PATH: ${{ secrets.VPS_PATH || '/home/bylearner/app/CodingTools' }} | |
| WEB_ROOT: ${{ secrets.WEB_ROOT || '/var/www/coding-tools' }} | |
| DEPLOY_REF: ${{ github.sha }} | |
| run: | | |
| ssh -i ~/.ssh/deploy_key -o IdentitiesOnly=yes -p "${VPS_PORT}" "${VPS_USER}@${VPS_HOST}" \ | |
| "DEPLOY_REF='${DEPLOY_REF}' VPS_PATH='${VPS_PATH}' WEB_ROOT='${WEB_ROOT}' SERVICE_USER='${VPS_USER}' bash -se" <<'REMOTE' | |
| set -euo pipefail | |
| if [ -f "$HOME/.nvm/nvm.sh" ]; then | |
| . "$HOME/.nvm/nvm.sh" | |
| fi | |
| cd "$VPS_PATH" | |
| git fetch --prune origin master | |
| git reset --hard "$DEPLOY_REF" | |
| npm ci | |
| npm run build | |
| test -f "$VPS_PATH/dist/index.html" | |
| test -f "$VPS_PATH/dist/404.html" | |
| test -f "$VPS_PATH/dist/.well-known/agent-card.json" | |
| npm run a2a:test | |
| NODE_BIN="$(command -v node)" | |
| sudo tee /etc/systemd/system/coding-tools-a2a.service > /dev/null <<EOF | |
| [Unit] | |
| Description=Coding.Tools A2A runtime | |
| After=network.target | |
| [Service] | |
| Type=simple | |
| User=$SERVICE_USER | |
| WorkingDirectory=$VPS_PATH | |
| Environment=NODE_ENV=production | |
| Environment=HOST=127.0.0.1 | |
| Environment=PORT=5510 | |
| ExecStart=$NODE_BIN server/a2a-runtime.js | |
| Restart=always | |
| RestartSec=5 | |
| [Install] | |
| WantedBy=multi-user.target | |
| EOF | |
| sudo systemctl daemon-reload | |
| sudo systemctl enable coding-tools-a2a | |
| sudo systemctl restart coding-tools-a2a | |
| sudo mkdir -p "$WEB_ROOT" | |
| sudo rsync -a --delete --chown=www-data:www-data --chmod=D755,F644 "$VPS_PATH/dist/" "$WEB_ROOT/" | |
| pkill -f '[e]leventy.*serve' 2>/dev/null || true | |
| sudo tee /etc/nginx/sites-available/default > /dev/null <<EOF | |
| server { | |
| listen 80 default_server; | |
| listen [::]:80 default_server; | |
| root $WEB_ROOT; | |
| index index.html; | |
| error_page 404 /404.html; | |
| location = /.well-known/agent-card.json { | |
| expires 5m; | |
| add_header Cache-Control "public, max-age=300"; | |
| try_files \$uri =404; | |
| } | |
| location / { | |
| try_files \$uri \$uri/ \$uri.html =404; | |
| } | |
| location = /a2a { | |
| proxy_pass http://127.0.0.1:5510/a2a; | |
| proxy_http_version 1.1; | |
| 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; | |
| } | |
| location ^~ /a2a/ { | |
| proxy_pass http://127.0.0.1:5510/a2a/; | |
| proxy_http_version 1.1; | |
| 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; | |
| } | |
| location = /mcp { | |
| proxy_pass http://127.0.0.1:5510/mcp; | |
| proxy_http_version 1.1; | |
| 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; | |
| } | |
| location ~* \.(css|js|svg|png|jpg|jpeg|gif|ico|json|webp|wasm|onnx)$ { | |
| expires 1y; | |
| add_header Cache-Control "public, immutable"; | |
| try_files \$uri =404; | |
| } | |
| } | |
| EOF | |
| sudo nginx -t | |
| sudo systemctl reload nginx | |
| curl -fsS -o /dev/null http://localhost:80/ | |
| curl -fsS -o /dev/null http://localhost:80/.well-known/agent-card.json | |
| curl -fsS -o /dev/null http://localhost:80/a2a/healthz | |
| curl -fsS -o /dev/null \ | |
| -H 'Content-Type: application/a2a+json' \ | |
| -H 'A2A-Version: 1.0' \ | |
| -d '{"message":{"role":"ROLE_USER","messageId":"deploy-smoke","metadata":{"toolId":"sha256-generator"},"parts":[{"text":"abc"}]}}' \ | |
| http://localhost:80/a2a/message:send | |
| curl -fsS -o /dev/null \ | |
| -H 'Content-Type: application/json' \ | |
| -H 'Accept: application/json, text/event-stream' \ | |
| -H 'MCP-Protocol-Version: 2025-06-18' \ | |
| -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"sha256-generator","arguments":{"input":"abc"}}}' \ | |
| http://localhost:80/mcp | |
| REMOTE | |
| - name: Verify public site | |
| env: | |
| PUBLIC_URL: ${{ secrets.PUBLIC_URL || 'http://35.239.243.94/' }} | |
| run: | | |
| curl -fsS --location --max-time 20 --output /dev/null "${PUBLIC_URL}" |