feat: startup-quality product page + scripts v3.3.8 #43
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: π§ͺ CI β Lint & Health | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| ci: | |
| name: Node ${{ matrix.node }} on ubuntu-latest | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node: ['20', '22'] | |
| fail-fast: false | |
| steps: | |
| - name: β¬οΈ Checkout | |
| uses: actions/checkout@v4 | |
| - name: π’ Setup Node.js ${{ matrix.node }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| # ββ Shell syntax checks ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π Shell syntax check (bash -n) | |
| run: | | |
| set -euo pipefail | |
| FAIL=0 | |
| for f in \ | |
| setup.sh \ | |
| setup-termux-widget.sh \ | |
| install-termux.sh \ | |
| update.sh \ | |
| start.sh \ | |
| doctor.sh \ | |
| bin/isotope; do | |
| if [ -f "$f" ]; then | |
| if bash -n "$f" 2>/dev/null; then | |
| echo "β $f" | |
| else | |
| echo "β $f β syntax error" | |
| bash -n "$f" || true | |
| FAIL=1 | |
| fi | |
| else | |
| echo "β οΈ $f β not found (skipping)" | |
| fi | |
| done | |
| [ "$FAIL" -eq 0 ] || exit 1 | |
| - name: π ShellCheck | |
| run: | | |
| set -euo pipefail | |
| if ! command -v shellcheck >/dev/null 2>&1; then | |
| echo "Installing shellcheck..." | |
| sudo apt-get install -y shellcheck >/dev/null 2>&1 | |
| fi | |
| echo "shellcheck $(shellcheck --version | head -2 | tail -1)" | |
| FAIL=0 | |
| for f in \ | |
| setup.sh \ | |
| setup-termux-widget.sh \ | |
| install-termux.sh \ | |
| update.sh \ | |
| start.sh \ | |
| doctor.sh \ | |
| bin/isotope; do | |
| if [ -f "$f" ]; then | |
| # SC1090: can't follow sourced file β expected in this codebase | |
| # SC1091: not following include β expected | |
| # SC2148: no shebang for sourced files β not applicable | |
| if shellcheck -x -e SC1090,SC1091 "$f"; then | |
| echo "β $f" | |
| else | |
| echo "β οΈ $f β shellcheck warnings (non-fatal for now)" | |
| fi | |
| else | |
| echo "β οΈ $f β not found (skipping)" | |
| fi | |
| done | |
| # ShellCheck is advisory in CI for now β uncomment to make it block: | |
| # [ "$FAIL" -eq 0 ] || exit 1 | |
| # ββ Node syntax ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: β Node.js syntax check β server.mjs | |
| run: node --check server.mjs | |
| # ββ Required files βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π Required files present | |
| run: | | |
| set -euo pipefail | |
| FAIL=0 | |
| REQUIRED=( | |
| server.mjs | |
| package.json | |
| setup.sh | |
| setup.bat | |
| install.ps1 | |
| install-termux.sh | |
| setup-termux-widget.sh | |
| update.sh | |
| update.bat | |
| start.sh | |
| start.bat | |
| doctor.sh | |
| doctor.bat | |
| bin/isotope | |
| bin/isotope.bat | |
| isotope-complete.sql | |
| .env.example | |
| README.md | |
| CHANGELOG.md | |
| TERMUX_WIDGET.md | |
| ) | |
| for f in "${REQUIRED[@]}"; do | |
| if [ -f "$f" ]; then | |
| echo "β $f" | |
| else | |
| echo "β $f β MISSING" | |
| FAIL=1 | |
| fi | |
| done | |
| [ "$FAIL" -eq 0 ] || exit 1 | |
| # ββ No hardcoded secrets βββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π No hardcoded secrets | |
| run: | | |
| set -euo pipefail | |
| if grep -rE 'SUPABASE_SERVICE_ROLE_KEY\s*=\s*eyJ' \ | |
| --include="*.mjs" --include="*.js" --include="*.sh" .; then | |
| echo "β Hardcoded service-role key detected!" | |
| exit 1 | |
| fi | |
| echo "β No hardcoded secrets" | |
| # ββ Schema stats βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π Schema file stats | |
| run: | | |
| set -euo pipefail | |
| for f in isotope-complete.sql community-patch-v4.sql performance-patch.sql; do | |
| if [ -f "$f" ]; then | |
| echo "=== $f ===" | |
| echo " Size : $(wc -c < "$f") bytes" | |
| echo " Lines : $(wc -l < "$f")" | |
| echo " Tables : $(grep -c 'CREATE TABLE' "$f" || echo 0)" | |
| echo " Funcs : $(grep -c 'CREATE OR REPLACE FUNCTION' "$f" || echo 0)" | |
| echo " Policies: $(grep -c 'CREATE POLICY' "$f" || echo 0)" | |
| else | |
| echo "β οΈ $f not found β skipping" | |
| fi | |
| done | |
| # ββ Termux simulation βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π± Termux simulation β platform detection | |
| run: | | |
| set -euo pipefail | |
| echo "Simulating Termux environment..." | |
| export TERMUX_VERSION="ci-test" | |
| export PREFIX="/data/data/com.termux/files/usr" | |
| # Test that setup.sh correctly detects 'termux' platform when vars are set | |
| DETECTED=$( | |
| TERMUX_VERSION="$TERMUX_VERSION" PREFIX="$PREFIX" \ | |
| node -e " | |
| const { execSync } = require('child_process'); | |
| const out = execSync( | |
| 'bash -c \\'source setup.sh --yes --no-start --termux-ci 2>&1 | head -5 || true\\'', | |
| { env: { ...process.env, TERMUX_VERSION: \"ci-test\", PREFIX: \"/data/data/com.termux/files/usr\" }, timeout: 10000 } | |
| ).toString().trim(); | |
| console.log(out); | |
| " 2>/dev/null || echo "skipped" | |
| ) | |
| echo "Termux simulation output (first 5 lines):" | |
| echo "$DETECTED" | head -5 | |
| echo "β Termux simulation completed (full install requires real Android device)" | |
| # ββ Smoke test ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π Smoke test β server starts and responds | |
| run: | | |
| set -uo pipefail | |
| SUPABASE_URL=https://placeholder.supabase.co \ | |
| SUPABASE_ANON_KEY=placeholder_anon_key \ | |
| SESSION_SECRET=ci_smoke_test_secret_not_real \ | |
| ADMIN_SECRET=ci_admin_placeholder \ | |
| NODE_ENV=test \ | |
| PORT=3099 \ | |
| node server.mjs & | |
| SERVER_PID=$! | |
| echo "Server PID: $SERVER_PID β polling /api/health..." | |
| STATUS="000" | |
| for i in $(seq 1 40); do | |
| sleep 0.5 | |
| STATUS=$(curl -s -o /dev/null -w "%{http_code}" \ | |
| http://127.0.0.1:3099/api/health 2>/dev/null || echo "000") | |
| if [ "$STATUS" != "000" ]; then | |
| echo " HTTP $STATUS after ${i} attempts" | |
| break | |
| fi | |
| printf ' Attempt %d: waiting...\n' "$i" | |
| done | |
| kill "$SERVER_PID" 2>/dev/null || true | |
| wait "$SERVER_PID" 2>/dev/null || true | |
| echo "Final health status: $STATUS" | |
| case "$STATUS" in | |
| 2*|3*|4*|5*) | |
| echo "β Server started and responded (HTTP $STATUS)" | |
| ;; | |
| *) | |
| echo "β Server did not respond (got: $STATUS)" | |
| exit 1 | |
| ;; | |
| esac | |
| # ββ Release zip simulation ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π¦ Release zip β build + verify extract | |
| run: | | |
| set -euo pipefail | |
| VERSION="$(node -e "process.stdout.write(require('./package.json').version)")" | |
| echo "Building release zip for v$VERSION..." | |
| zip -qr "isotope-v${VERSION}-ci-test.zip" . \ | |
| --exclude ".git/*" \ | |
| --exclude "node_modules/*" \ | |
| --exclude ".env" \ | |
| --exclude "*.log" \ | |
| --exclude ".github/*" \ | |
| --exclude "screenshots/*" \ | |
| --exclude "*.zip" | |
| echo "Archive: $(wc -c < "isotope-v${VERSION}-ci-test.zip") bytes" | |
| # Extract to a fresh dir and check required files are present | |
| TMPDIR="$(mktemp -d)" | |
| unzip -q "isotope-v${VERSION}-ci-test.zip" -d "$TMPDIR" | |
| echo "Verifying extracted release..." | |
| FAIL=0 | |
| for f in server.mjs package.json setup.sh install-termux.sh bin/isotope .env.example; do | |
| if [ -f "$TMPDIR/$f" ]; then | |
| echo " β $f" | |
| else | |
| echo " β $f β missing from zip" | |
| FAIL=1 | |
| fi | |
| done | |
| rm -rf "$TMPDIR" "isotope-v${VERSION}-ci-test.zip" | |
| [ "$FAIL" -eq 0 ] || exit 1 | |
| echo "β Release zip contains all required files" | |
| # ββ Docs validation ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π Docs + README validation | |
| run: | | |
| set -euo pipefail | |
| node scripts/validate-docs.mjs || { | |
| echo "β οΈ Docs validation had errors β see output above" | |
| exit 1 | |
| } | |
| # ββ Upload logs on failure ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| - name: π€ Upload CI logs on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ci-logs-node${{ matrix.node }}-${{ github.run_id }} | |
| path: | | |
| ~/.isotope/logs/ | |
| /tmp/isotope-ci-*.log | |
| if-no-files-found: ignore | |
| retention-days: 7 |