Reliability / correctness:
- Always arm the graceful-shutdown safety timeout. Previously
`shutdown(force=true)` (called from uncaughtException) skipped the
timeout entirely, so a hung `framework.stop()` after a crash would
wedge the process until Docker's SIGKILL. Now uses 3s when forced,
8s otherwise, and .unref()s so it never blocks a clean exit.
- Attach a `.catch()` to `framework.start()` so a bad Webex token or
WebSocket handshake failure produces a clear "Webex framework failed
to start" error line instead of a bare Unhandled Rejection while the
bot silently stays dead.
- Rename MDM timestamp labels from "(EDT)" to "(ET)" since the
formatter uses DST-aware America/New_York (half the year it's EST).
Cleanup:
- Drop `body-parser` in favor of the built-in `express.json()`
(Express 4.16+). Removes one direct dep; still present as a
transitive dep of express itself.
- Remove orphaned JSDoc block referring to a helper that no longer
exists.
- Delete legacy `query-offline.js` (marked deprecated since the bot
`offline` command shipped) and remove its `APPSPACE_API_TOKEN` /
`APPSPACE_BASE_URL` env vars from `.env.example` and the
`offline:legacy` npm script from `package.json`.
Config / metadata:
- Add `"engines": { "node": ">=20" }` to package.json so npm warns on
the wrong Node version instead of just the README saying so.
- Document `SMOKE_TEST=true` in `.env.example`.
Docs:
- Rewrite README to document the `restart-offline` command (iOS
Supervised requirement, 50-device cap, concurrency, audit log
fields, fresh-at-execute semantics), the ET-not-EDT labeling,
structured error logging, character-budget rendering, and the
new CI workflow. Refresh the TODO section to reflect what has
actually shipped.
CI:
- Add `.gitea/workflows/ci.yml` with two jobs: syntax check
(`node --check` on index.js and mdm.js) and a Docker smoke test
that builds the production image, boots it with dummy credentials
+ SMOKE_TEST=true, and waits up to 30s for the container's
built-in healthcheck to reach `healthy`. Dumps container logs
on failure.
Co-authored-by: Cursor <cursoragent@cursor.com>
83 lines
2.3 KiB
YAML
83 lines
2.3 KiB
YAML
name: CI
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
jobs:
|
|
syntax:
|
|
name: Syntax check
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: '20'
|
|
cache: 'npm'
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Node --check on source files
|
|
run: |
|
|
set -e
|
|
node --check index.js
|
|
node --check mdm.js
|
|
|
|
docker-smoke:
|
|
name: Docker build + healthcheck smoke test
|
|
runs-on: ubuntu-latest
|
|
needs: syntax
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Build production image
|
|
run: docker build -t appspace-webex:ci .
|
|
|
|
# Boot the container with dummy credentials + SMOKE_TEST=true so the Webex
|
|
# bot framework is skipped. The Dockerfile's built-in HEALTHCHECK hits /health;
|
|
# we poll `docker inspect` for the health status.
|
|
- name: Start container with dummy credentials
|
|
run: |
|
|
docker run -d --name smoke \
|
|
-e PORT=3000 \
|
|
-e NODE_ENV=production \
|
|
-e SMOKE_TEST=true \
|
|
-e WEBEX_BOT_TOKEN=smoke-test-bot-token \
|
|
-e WEBEX_ROOM_ID=smoke-test-room-id \
|
|
-e APPSPACE_INSTANCE_URL=https://smoke.example.com \
|
|
-e APPSPACE_SUBJECT_ID=smoke-subject \
|
|
-e APPSPACE_REFRESH_TOKEN=smoke-refresh-token \
|
|
-e APPSPACE_API_BASE_URL=https://smoke.example.com \
|
|
appspace-webex:ci
|
|
|
|
- name: Wait for container to become healthy
|
|
run: |
|
|
set -e
|
|
for i in $(seq 1 30); do
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' smoke 2>/dev/null || echo none)
|
|
echo "attempt $i: status=$status"
|
|
if [ "$status" = "healthy" ]; then
|
|
echo "Container reached healthy"
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "Container failed to reach healthy within 30s"
|
|
docker logs smoke
|
|
exit 1
|
|
|
|
- name: Container logs on success (for reference)
|
|
if: success()
|
|
run: docker logs smoke
|
|
|
|
- name: Container logs on failure
|
|
if: failure()
|
|
run: docker logs smoke || true
|
|
|
|
- name: Cleanup
|
|
if: always()
|
|
run: docker rm -f smoke || true
|