Merge pull request #26 from AdaInTheLab/feat/type-filter-endpoint #120
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 Lab API to Dreamhost 🧪 | |
| on: | |
| push: | |
| branches: ["main"] | |
| concurrency: | |
| group: deploy-lab-api | |
| cancel-in-progress: true | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: 🔍 Reveal NODE_ENV (runner) | |
| run: | | |
| echo "RUNNER NODE_ENV=$NODE_ENV" | |
| env | grep -E '^NODE_ENV=' || true | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: "npm" | |
| - name: Install dependencies (incl dev for tests) | |
| run: npm ci | |
| - name: Run tests | |
| run: npm test | |
| - name: Build API | |
| run: npm run build | |
| - name: Verify build output exists | |
| run: | | |
| set -e | |
| test -d dist | |
| ls -la dist | |
| - name: Create deploy bundle | |
| run: | | |
| set -e | |
| rm -rf deploy_bundle | |
| mkdir -p deploy_bundle | |
| cp -R dist deploy_bundle/dist | |
| cp -R content deploy_bundle/content | |
| cp package.json package-lock.json deploy_bundle/ | |
| # Include ecosystem file if it's tracked in the repo | |
| if [ -f ecosystem.config.cjs ]; then | |
| cp ecosystem.config.cjs deploy_bundle/ | |
| echo "✅ Included ecosystem.config.cjs (from repo)" | |
| else | |
| echo "⚠️ ecosystem.config.cjs not found in repo checkout; continuing without it" | |
| ls -la | |
| fi | |
| - name: Validate deploy secrets (fail fast) | |
| run: | | |
| test -n "${{ secrets.SFTP_HOST }}" || (echo "Missing SFTP_HOST" && exit 1) | |
| test -n "${{ secrets.SFTP_USER }}" || (echo "Missing SFTP_USER" && exit 1) | |
| test -n "${{ secrets.SFTP_PATH }}" || (echo "Missing SFTP_PATH" && exit 1) | |
| test -n "${{ secrets.SSH_PRIVATE_KEY }}" || (echo "Missing SSH_PRIVATE_KEY" && exit 1) | |
| test -n "${{ secrets.SSH_PORT }}" || (echo "Missing SSH_PORT" && exit 1) | |
| - name: Ensure remote deploy dir exists | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.SFTP_HOST }} | |
| username: ${{ secrets.SFTP_USER }} | |
| key: ${{ secrets.SSH_PRIVATE_KEY }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script_stop: true | |
| script: | | |
| set -e | |
| mkdir -p "${{ secrets.SFTP_PATH }}" | |
| mkdir -p "${{ secrets.SFTP_PATH }}/logs" | |
| mkdir -p "${{ secrets.SFTP_PATH }}/data" | |
| ls -ld "${{ secrets.SFTP_PATH }}" "${{ secrets.SFTP_PATH }}/logs" "${{ secrets.SFTP_PATH }}/data" | |
| - name: Upload deploy bundle via scp (key auth) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| PORT="${{ secrets.SSH_PORT }}" | |
| PORT="${PORT:-22}" | |
| mkdir -p ~/.ssh | |
| chmod 700 ~/.ssh | |
| echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 | |
| chmod 600 ~/.ssh/id_ed25519 | |
| ssh-keyscan -p "$PORT" -H "${{ secrets.SFTP_HOST }}" >> ~/.ssh/known_hosts | |
| scp -P "$PORT" -r deploy_bundle/* \ | |
| "${{ secrets.SFTP_USER }}@${{ secrets.SFTP_HOST }}:${{ secrets.SFTP_PATH }}/" | |
| - name: Restart API with PM2 (production + health check) | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.SFTP_HOST }} | |
| username: ${{ secrets.SFTP_USER }} | |
| key: ${{ secrets.SSH_PRIVATE_KEY }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script_stop: true | |
| script: | | |
| set -e | |
| echo "User: $(whoami)" | |
| echo "Host: $(hostname)" | |
| # Known-good Node/pm2 path | |
| export PATH="$HOME/.nvm/versions/node/v20.19.6/bin:$PATH" | |
| echo "PATH=$PATH" | |
| command -v node | |
| command -v npm | |
| command -v pm2 | |
| node -v | |
| npm -v | |
| pm2 -v | |
| cd "${{ secrets.SFTP_PATH }}" | |
| echo "🔍 Reveal NODE_ENV (server shell)" | |
| echo "SERVER NODE_ENV=$NODE_ENV" | |
| env | grep -E '^NODE_ENV=' || true | |
| echo "== Verify dist on server ==" | |
| test -f dist/index.js || (echo "❌ dist/index.js missing on server" && ls -la && ls -la dist || true && exit 1) | |
| mkdir -p logs data | |
| rm -f ecosystem.config.js || true | |
| echo "== Install production deps ==" | |
| npm ci --omit=dev | |
| echo "== PM2 startOrReload (production) ==" | |
| if [ -f ecosystem.config.cjs ]; then | |
| echo "✅ Using ecosystem.config.cjs" | |
| pm2 startOrReload ecosystem.config.cjs --env production --update-env | |
| echo "== PM2 env for process 0 (look for PORT/HOST) ==" | |
| pm2 env 0 | egrep -i '^(PORT|HOST|NODE_ENV|BASE_URL|API_PORT)=' || true | |
| echo "== What ports is node actually listening on? ==" | |
| ss -ltnp | grep node || true | |
| ss -ltnp | egrep ':(8001|3000|8000|8080)\b' || true | |
| echo "== Show the wrapper + ecosystem so we know what it runs ==" | |
| ls -la ecosystem.config.cjs pm2-start.cjs || true | |
| else | |
| echo "⚠️ No ecosystem.config.cjs found; fallback" | |
| pm2 restart lab-api --update-env || pm2 start dist/index.js --name lab-api --update-env | |
| fi | |
| pm2 save | |
| pm2 list | |
| echo "== Listening sockets (8001) ==" | |
| ss -ltnp | grep ':8001' || true | |
| echo "== Try both IPv4 + IPv6 ==" | |
| curl -v --max-time 2 "http://127.0.0.1:8001/health" || true | |
| curl -v --max-time 2 "http://[::1]:8001/health" || true | |
| curl -v --max-time 2 "http://localhost:8001/health" || true | |
| echo "== Health check (local) ==" | |
| for i in 1 2 3 4 5 6 7 8 9 10; do | |
| code="$(curl -s -o /tmp/health.txt -w "%{http_code}" "http://127.0.0.1:8001/health" || true)" | |
| if [ "$code" = "200" ]; then | |
| echo "✅ Health check OK (/health)" | |
| cat /tmp/health.txt || true | |
| exit 0 | |
| fi | |
| echo "…health not ready (attempt $i/10) http=$code" | |
| echo "--- body ---" | |
| cat /tmp/health.txt || true | |
| echo "------------" | |
| sleep 1 | |
| done | |
| echo "❌ Health check failed after retries" | |
| pm2 list || true | |
| pm2 describe lab-api || true | |
| pm2 logs lab-api --err --lines 200 --nostream || true | |
| pm2 logs lab-api --out --lines 200 --nostream || true | |
| exit 1 | |
| - name: On failure show PM2 logs (last 120 lines) | |
| if: failure() | |
| uses: appleboy/ssh-action@v1.0.3 | |
| with: | |
| host: ${{ secrets.SFTP_HOST }} | |
| username: ${{ secrets.SFTP_USER }} | |
| key: ${{ secrets.SSH_PRIVATE_KEY }} | |
| port: ${{ secrets.SSH_PORT }} | |
| script_stop: false | |
| script: | | |
| export PATH="$HOME/.nvm/versions/node/v20.19.6/bin:$PATH" | |
| pm2 list || true | |
| pm2 logs lab-api --out --lines 120 --nostream || true |