fix(docker): complete the Node and Python image builds #4
Workflow file for this run
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: Docker image smoke test | |
| # Builds every image the repository ships and checks each actually serves what | |
| # it claims. Reading a Dockerfile is not enough — both failures below were | |
| # invisible in review and obvious the moment anything ran the container: | |
| # | |
| # - the published image (#23) copied the widget to /app/static/fcaptcha.js | |
| # while the server only looked in client/, so /fcaptcha.js returned 404 for | |
| # three months while the file sat in the image untouched. | |
| # - server-node/ and server-python/ copied only server.js / server.py without | |
| # the modules they import, so both crashed on startup. Broken in the initial | |
| # release commit and never once built in the seven months since. | |
| # | |
| # Neither is catchable by a unit test: the mismatch lives between the Dockerfile | |
| # and the source, not inside either. | |
| on: | |
| push: | |
| branches: [main] | |
| paths: ['docker/**', 'server-go/**', 'server-node/**', 'server-python/**', 'client/**', '.github/workflows/docker-smoke.yml'] | |
| pull_request: | |
| paths: ['docker/**', 'server-go/**', 'server-node/**', 'server-python/**', 'client/**', '.github/workflows/docker-smoke.yml'] | |
| jobs: | |
| smoke: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: published (Go) | |
| dockerfile: docker/Dockerfile | |
| demo: true | |
| - name: node | |
| dockerfile: server-node/Dockerfile | |
| demo: false | |
| - name: python | |
| dockerfile: server-python/Dockerfile | |
| demo: false | |
| name: ${{ matrix.name }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| # Always from the repository root: every image needs client/fcaptcha.js, | |
| # which sits outside the per-server directories. | |
| - name: Build | |
| run: docker build -f ${{ matrix.dockerfile }} -t smoke-img . | |
| - name: Start | |
| run: | | |
| docker run -d --name smoke -p 3000:3000 smoke-img | |
| for _ in $(seq 1 60); do | |
| curl -sf http://localhost:3000/health >/dev/null && exit 0 | |
| sleep 0.5 | |
| done | |
| echo "::error::container never became healthy — it may have crashed on startup" | |
| docker logs smoke | |
| exit 1 | |
| - name: Serves the widget | |
| run: | | |
| code=$(curl -s -o /tmp/w.js -w '%{http_code}' http://localhost:3000/fcaptcha.js) | |
| [ "$code" = "200" ] || { echo "::error::/fcaptcha.js returned $code — the image ships the widget but does not serve it"; docker logs smoke; exit 1; } | |
| grep -q "FCaptcha" /tmp/w.js || { echo "::error::/fcaptcha.js served something that is not the widget"; exit 1; } | |
| echo "widget served, $(wc -c < /tmp/w.js) bytes" | |
| - name: Issues a proof-of-work challenge | |
| run: | | |
| curl -sf "http://localhost:3000/api/pow/challenge?siteKey=smoke" -o /tmp/c.json \ | |
| || { echo "::error::the challenge endpoint did not respond"; docker logs smoke; exit 1; } | |
| grep -q challengeId /tmp/c.json || { echo "::error::challenge response has no challengeId"; cat /tmp/c.json; exit 1; } | |
| - name: No missing-widget warning in the startup log | |
| run: | | |
| if docker logs smoke 2>&1 | grep -q "will return 404"; then | |
| echo "::error::server logged that it could not find the widget" | |
| docker logs smoke; exit 1 | |
| fi | |
| # The demo page loads the widget from /fcaptcha.js, so it was collateral | |
| # damage in #23 — 200 with a widget that never initialised. | |
| - name: Demo page loads and its widget reference resolves | |
| if: matrix.demo | |
| run: | | |
| curl -sf http://localhost:3000/demo/ -o /tmp/demo.html || { echo "::error::/demo/ did not load"; exit 1; } | |
| src=$(grep -o 'src="[^"]*fcaptcha[^"]*"' /tmp/demo.html | head -1 | sed 's/src="//;s/"//') | |
| echo "demo loads the widget from: $src" | |
| curl -sf "http://localhost:3000${src}" >/dev/null || { echo "::error::the demo's widget URL $src does not resolve"; exit 1; } | |
| - name: End-to-end detection suite against the container | |
| run: node test/test-detection.js || true # Go and Python have documented divergences; startup and routing are what this job guards | |
| - name: Cleanup | |
| if: always() | |
| run: docker rm -f smoke || true |