From 1789faf21159b9b3763eb9657c9df15a9b09d41b Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 12 Feb 2026 10:46:25 +0100 Subject: [PATCH 1/5] feat: dockerize the site Note: this removes the non-docker configuration, to reduce maintenance. Development version runs in two containers in order to simplify live-reload. Production version will run in a single container. See README.md for details of usage. Test-bot: skip --- .gitattributes | 4 + .github/workflows/test.yml | 6 +- .gitignore | 13 ++- README.md | 49 ++++---- _control/.keep | 0 build.sh | 92 +++++++++++++++ package.json | 10 -- public.Dockerfile | 13 +++ public/package-lock.json | 120 ++------------------ public/package.json | 2 - public/src/environments/environment.prod.ts | 2 +- public/src/environments/environment.ts | 10 +- resources/start-public.sh | 28 +++++ resources/start-server.sh | 59 ++++++++++ resources/test.sh | 17 +++ server.Dockerfile | 13 +++ server/_start_server.bat | 4 - server/_start_server.sh | 5 - server/code.ts | 2 +- server/package-lock.json | 57 ++++++---- server/package.json | 7 -- server/tsconfig.json | 5 +- 22 files changed, 325 insertions(+), 193 deletions(-) create mode 100644 .gitattributes create mode 100644 _control/.keep create mode 100755 build.sh create mode 100644 public.Dockerfile create mode 100755 resources/start-public.sh create mode 100755 resources/start-server.sh create mode 100755 resources/test.sh create mode 100644 server.Dockerfile delete mode 100644 server/_start_server.bat delete mode 100755 server/_start_server.sh diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4622295 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ + +Dockerfile text eol=lf +*.Dockerfile text eol=lf +*.sh eol=lf diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7c060de..1d91ee6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,10 +25,8 @@ jobs: - name: Set up Node.js version uses: actions/setup-node@v1 with: - node-version: '20.x' + node-version: '22.x' - name: npm install, build, and test run: | - npm install - npm run build - npm run test + ./build.sh configure build test --release diff --git a/.gitignore b/.gitignore index 52c0661..5b7278f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,15 @@ localenv.sh node_modules/ .keymanapp-test-bot.* -local*.sh \ No newline at end of file +local*.sh + +# Shared files are bootstrapped: +resources/bootstrap.inc.sh +resources/.bootstrap-version +resources/.bootstrap-registry +_common/ + +# State files +_control/debug +_control/release +_control/ready diff --git a/README.md b/README.md index ecd8ece..cc3f0e8 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,13 @@ git clone https://github.com/keymanapp/status.keyman.com cd status.keyman.com/ ``` -Build status.keyman.com: +### Prerequisites -```bash -cd server -npm install -npm run-script build -cd ../public -npm install -npm run-script build -cd .. -``` +* Docker Desktop +* On Windows, you'll need to have Git Bash installed in `C:\Program Files\git\bin\bash.exe`. -Before running the node server, you need to have two API tokens set as environment variables. You might want to add these to script `server/localenv.sh`. +Before building and starting the site, you need to have API tokens set as +environment variables. These should be added in script `server/localenv.sh`. ```bash export KEYMANSTATUS_TEAMCITY_TOKEN=[your personal auth token here] @@ -27,25 +21,33 @@ export KEYMANSTATUS_GITHUB_TOKEN=[your personal auth token here] export KEYMANSTATUS_SENTRY_TOKEN=[your personal auth token here] ``` -On Windows, you'll also need to have Git Bash installed in `C:\Program Files\git\bin\bash.exe`. - -## Development server +## Build the docker containers -This repo is configured for live build and reload of both the client and server. You'll need two terminals open. In the first, run: +Build status.keyman.com, in development mode: ```bash -npm run start-server +./build.sh stop build --debug ``` -and in the second, run: +## Start the development server + +This repo is configured for live build and reload of both the client and server, +running in Docker. ```bash -npm run start-client +./build.sh start --debug ``` -* Point your browser to to view the live reload version of the application. -* The query parameter `?c=1` adds a contributions view which is not visible by default. -* Another query parameter `?sprint=P8S4` parameter to view sprint contributions data for P8S4 +* Point your browser to to view the live reload version + of the application. +* The following query parameters are available: + * `?c=1` shows contributions at the top center + * `?o=1` shows owner for each platform + * `?a=1` shows build agent status at the top right + * `?r=1` adds a refresh button to force a server-side full refresh (this is + costly, so only press this when there has been a data error such as a + network failure making status data out of date; most errors are + actually self-healing) ### @keymanapp-test-bot @@ -54,3 +56,8 @@ Three files needed for development: * `.keymanapp-test-bot.appid`: integer appid (e.g. 134443 for the normal test app) * `.keymanapp-test-bot.pem`: certificate for GitHub integration for app * `.keymanapp-test-bot.secret`: secret for GitHub integration for app + +## Production + +This site is deployed to a Kubernetes cluster via configuration in a private +repo to status.keyman.com. diff --git a/_control/.keep b/_control/.keep new file mode 100644 index 0000000..e69de29 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..0d177d6 --- /dev/null +++ b/build.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash +## START STANDARD SITE BUILD SCRIPT INCLUDE +readonly THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" +readonly BOOTSTRAP="$(dirname "$THIS_SCRIPT")/resources/bootstrap.inc.sh" +readonly BOOTSTRAP_VERSION=v1.0.9 +[ -f "$BOOTSTRAP" ] && source "$BOOTSTRAP" || source <(curl -H "Cache-Control: no-cache" -fs https://raw.githubusercontent.com/keymanapp/shared-sites/$BOOTSTRAP_VERSION/bootstrap.inc.sh) +## END STANDARD SITE BUILD SCRIPT INCLUDE + +# +# in development mode, the public host is on a separate port to reduce the +# complexity of tsc --watch vs nodemon; in the future we could consider +# consolidation into a single container. There is little benefit into splitting +# production into two containers, given production version of public is fully +# static on server side. +# + +readonly HOST_STATUS_KEYMAN_COM=status.keyman.com.localhost + +readonly THIS_CONTAINER_NAME=status-keyman-website +readonly PUBLIC_CONTAINER_NAME=status-keyman-public +readonly THIS_CONTAINER_DESC=status-keyman-com-app +readonly PUBLIC_CONTAINER_DESC=status-keyman-com-public +readonly THIS_IMAGE_NAME=status-keyman-website +readonly PUBLIC_IMAGE_NAME=status-keyman-public +readonly THIS_HOST="$HOST_STATUS_KEYMAN_COM" +readonly PUBLIC_HOST="$HOST_STATUS_KEYMAN_COM" # same host +readonly THIS_PORT="$PORT_STATUS_KEYMAN_COM" +readonly PUBLIC_PORT="$PORT_STATUS_KEYMAN_COM_PUBLIC" + +source _common/keyman-local-ports.inc.sh +source _common/docker.inc.sh + +################################ Main script ################################ + +builder_describe \ + "Setup status.keyman.com site to run via Docker." \ + configure \ + clean \ + build \ + start \ + stop \ + test + +builder_parse "$@" + +function build_docker_containers() { + build_docker_container $THIS_IMAGE_NAME $THIS_CONTAINER_NAME $BUILDER_CONFIGURATION server.Dockerfile + if builder_is_debug_build; then + build_docker_container $PUBLIC_IMAGE_NAME $PUBLIC_CONTAINER_NAME $BUILDER_CONFIGURATION public.Dockerfile + fi +} + +function start_docker_containers() { + start_docker_container $THIS_IMAGE_NAME $THIS_CONTAINER_NAME $THIS_CONTAINER_DESC $THIS_HOST $THIS_PORT $BUILDER_CONFIGURATION + if builder_is_debug_build; then + start_docker_container $PUBLIC_IMAGE_NAME $PUBLIC_CONTAINER_NAME $PUBLIC_CONTAINER_DESC $PUBLIC_HOST $PUBLIC_PORT $BUILDER_CONFIGURATION + fi +} + +function stop_docker_containers() { + rm -f ./_control/ready + stop_docker_container $THIS_IMAGE_NAME $THIS_CONTAINER_NAME + # always stop all containers - even in release build + stop_docker_container $PUBLIC_IMAGE_NAME $PUBLIC_CONTAINER_NAME +} + +function clean_docker_containers() { + clean_docker_container $THIS_IMAGE_NAME $THIS_CONTAINER_NAME + # always clean all containers - even in release build + clean_docker_container $PUBLIC_IMAGE_NAME $PUBLIC_CONTAINER_NAME +} + +function do_clean() { + clean_docker_containers + rm -rf ./server/node_modules ./server/dist + rm -rf ./public/node_modules ./public/angular ./public/dist + rm -rf ./public/angular + rm -rf ./_common + rm -rf ./resources/.bootstrap-registry ./resources/bootstrap-version ./resources/bootstrap.inc.sh +} + +function test_docker_container() { + stop_docker_container $THIS_IMAGE_NAME $THIS_CONTAINER_NAME + MSYS_NO_PATHCONV=1 start_docker_container $THIS_IMAGE_NAME $THIS_CONTAINER_NAME $THIS_CONTAINER_DESC $THIS_HOST $THIS_PORT $BUILDER_CONFIGURATION "/bin/sh" "./resources/test.sh" +} + +builder_run_action configure bootstrap_configure +builder_run_action clean do_clean +builder_run_action stop stop_docker_containers +builder_run_action build build_docker_containers +builder_run_action start start_docker_containers +builder_run_action test test_docker_container diff --git a/package.json b/package.json index fe94296..a209ad8 100644 --- a/package.json +++ b/package.json @@ -2,17 +2,7 @@ "name": "keyman-status", "version": "1.0.0", "description": "Report on status of Keyman projects", - "main": "server/dist/server/code.js", "type": "module", - "scripts": { - "build": "npm run-script build-server && npm run-script build-client", - "build-client": "cd public && npm install && npm run-script build", - "build-server": "cd server && npm install && npm run-script build", - "start-client": "cd public && npm run-script start", - "start-server": "cd server && npm run-script start", - "test": "cd server && npx mocha --import=./_mocha_register.js", - "log": "az webapp log tail --resource-group keyman --name com-keyman-status" - }, "author": "Marc Durdin", "license": "MIT" } diff --git a/public.Dockerfile b/public.Dockerfile new file mode 100644 index 0000000..35fe571 --- /dev/null +++ b/public.Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1 + +FROM node:22@sha256:cd6fb7efa6490f039f3471a189214d5f548c11df1ff9e5b181aa49e22c14383e AS node-builder + +WORKDIR /var/www/html + +ARG BUILDER_CONFIGURATION="debug" +ENV BUILDER_CONFIGURATION=$BUILDER_CONFIGURATION + +# DOCKER_RUNTIME_PUBLIC env var helps prevent the resource scripts running on the host +ENV DOCKER_RUNTIME_PUBLIC=1 + +CMD [ "/bin/sh", "./resources/start-public.sh" ] diff --git a/public/package-lock.json b/public/package-lock.json index 64284ac..fdf84a6 100644 --- a/public/package-lock.json +++ b/public/package-lock.json @@ -1218,9 +1218,9 @@ } }, "node_modules/@angular/build/node_modules/@types/node": { - "version": "25.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", - "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, "license": "MIT", "optional": true, @@ -1932,9 +1932,9 @@ } }, "node_modules/@angular/cli/node_modules/@types/node": { - "version": "25.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", - "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, "license": "MIT", "optional": true, @@ -3783,19 +3783,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -8091,14 +8078,6 @@ "node": ">=0.1.90" } }, - "node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - }, "node_modules/compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -10289,18 +10268,6 @@ "colors": "1.4.0" } }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, "node_modules/jose": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", @@ -13331,27 +13298,6 @@ "node": ">=18" } }, - "node_modules/terser": { - "version": "5.44.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", - "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -14856,9 +14802,9 @@ "requires": {} }, "@types/node": { - "version": "25.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", - "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, "optional": true, "peer": true, @@ -15227,9 +15173,9 @@ } }, "@types/node": { - "version": "25.0.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", - "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", "dev": true, "optional": true, "peer": true, @@ -16302,18 +16248,6 @@ "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true }, - "@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, "@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", @@ -18814,14 +18748,6 @@ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "optional": true, - "peer": true - }, "compare-versions": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", @@ -20339,14 +20265,6 @@ "colors": "1.4.0" } }, - "jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, - "optional": true, - "peer": true - }, "jose": { "version": "6.1.3", "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", @@ -22419,20 +22337,6 @@ } } }, - "terser": { - "version": "5.44.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.1.tgz", - "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - } - }, "tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", diff --git a/public/package.json b/public/package.json index ce67448..0fc7ae7 100644 --- a/public/package.json +++ b/public/package.json @@ -3,8 +3,6 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "start": "ng serve --configuration development", - "build": "ng build --configuration production", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" diff --git a/public/src/environments/environment.prod.ts b/public/src/environments/environment.prod.ts index 8f02170..013a113 100644 --- a/public/src/environments/environment.prod.ts +++ b/public/src/environments/environment.prod.ts @@ -2,5 +2,5 @@ export const environment = { production: true, statusUrl: '/status', refreshBackendUrl: '/refresh', - webSocketUrl: 'wss://'+window.location.host + webSocketUrl: (window.location.protocol == 'http:' ? 'ws' : 'wss') + '://'+window.location.host }; diff --git a/public/src/environments/environment.ts b/public/src/environments/environment.ts index cc7a59e..854cf7e 100644 --- a/public/src/environments/environment.ts +++ b/public/src/environments/environment.ts @@ -2,14 +2,16 @@ // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. // The list of file replacements can be found in `angular.json`. +import { isDevMode } from "@angular/core"; + export const environment = { production: false, - // When running in development with ng watch on 4200 and nodemon on 3000, otherwise + // When running in development with ng watch on http:8061 and nodemon on 8060, otherwise // assume we are on the same host as the status backend - statusUrl: window.location.host == 'localhost:4200' ? '//localhost:3000/status' : '/status', - refreshBackendUrl: window.location.host == 'localhost:4200' ? '//localhost:3000/refresh' : '/refresh', - webSocketUrl: window.location.host == 'localhost:4200' ? 'ws://localhost:3000' : 'ws://'+window.location.host, + statusUrl: isDevMode() ? '//localhost:8060/status' : '/status', + refreshBackendUrl: isDevMode() ? '//localhost:8060/refresh' : '/refresh', + webSocketUrl: isDevMode() ? 'ws://localhost:8060' : 'ws://'+window.location.host, }; /* diff --git a/resources/start-public.sh b/resources/start-public.sh new file mode 100755 index 0000000..766d6e7 --- /dev/null +++ b/resources/start-public.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -eu + +if [ -z "${DOCKER_RUNTIME_PUBLIC+x}" ]; then + echo "ERROR: resources/start-public.sh should only be run in the public container" + exit 1 +fi + +if [ "$BUILDER_CONFIGURATION" = release ]; then + echo "ERROR: resources/start-public.sh should only be run in debug configurations" + exit 1 +fi + +# Configuration + +echo "Initializing debug frontend" + +cd public +npm install +cd .. + +echo "Starting debug frontend" + +export NODE_ENV=development + +cd public +./node_modules/.bin/ng serve --configuration development --verbose --watch=true --host=0.0.0.0 --poll 1000 --port 80 diff --git a/resources/start-server.sh b/resources/start-server.sh new file mode 100755 index 0000000..c8484ab --- /dev/null +++ b/resources/start-server.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +set -eu + +# Configuration + +echo "Initializing server with $BUILDER_CONFIGURATION configuration" + +if [ -z "${DOCKER_RUNTIME_SERVER+x}" ]; then + echo "ERROR: resources/start-server.sh should only be run in the server container" + exit 1 +fi + +rm -f _control/ready + +if [ "$BUILDER_CONFIGURATION" = release ]; then + # For a release build, we need to build public/ because it is served from the + # same container + echo "- Building public for release" + cd public + npm install + ./node_modules/.bin/ng build --configuration production + cd .. + + echo "- Building server for release" + cd server + npm install + ./node_modules/.bin/tsc + cd .. +else + # For a debug build, we only need to build server/, because public/ is served + # from a separate container, and we don't need to build first, because that is + # done with tsc-watch + echo "- Preparing server for debug" + cd server + npm install + cd .. +fi + +echo "Starting server with $BUILDER_CONFIGURATION configuration" + +if [ -f ./server/localenv.sh ]; then + . ./server/localenv.sh +fi + +touch _control/ready + +if [ "$BUILDER_CONFIGURATION" = release ]; then + # TODO: support staging env + export NODE_ENV=production + cd server/dist/server/ + + # TODO: make code.js pwd-safe + node code.js +else + export NODE_ENV=development + cd server + ./node_modules/.bin/tsc-watch --onSuccess "node ." --onFailure "node ." +fi diff --git a/resources/test.sh b/resources/test.sh new file mode 100755 index 0000000..bdd3fd8 --- /dev/null +++ b/resources/test.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +set -eu + +# Test + +if [ -z "${DOCKER_RUNTIME_SERVER+x}" ]; then + echo "ERROR: resources/test.sh should only be run in the server container" + exit 1 +fi + +echo "Testing server with $BUILDER_CONFIGURATION configuration" + +cd server + +npm install +npx mocha --import=./_mocha_register.js \ No newline at end of file diff --git a/server.Dockerfile b/server.Dockerfile new file mode 100644 index 0000000..06863fc --- /dev/null +++ b/server.Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1 + +FROM node:22@sha256:cd6fb7efa6490f039f3471a189214d5f548c11df1ff9e5b181aa49e22c14383e AS node-builder + +WORKDIR /var/www/html + +ARG BUILDER_CONFIGURATION="release" +ENV BUILDER_CONFIGURATION=$BUILDER_CONFIGURATION + +# DOCKER_RUNTIME_SERVER env var helps prevent the resource scripts running on the host +ENV DOCKER_RUNTIME_SERVER=1 + +CMD [ "/bin/sh", "./resources/start-server.sh" ] diff --git a/server/_start_server.bat b/server/_start_server.bat deleted file mode 100644 index 02ea911..0000000 --- a/server/_start_server.bat +++ /dev/null @@ -1,4 +0,0 @@ -@echo off -"%ProgramFiles%\git\bin\bash.exe" --init-file "c:\Program Files\Git\etc\profile" -l "./_start_server.sh" -if errorlevel 1 exit /b 1 -exit /b 0 diff --git a/server/_start_server.sh b/server/_start_server.sh deleted file mode 100755 index 9643ab4..0000000 --- a/server/_start_server.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -set -e -[ -f ./localenv.sh ] && . ./localenv.sh -./node_modules/.bin/tsc-watch --onSuccess "node ." --onFailure "node ." -# npx nodemon -w src -e ts,js --exec "npm run build-server" diff --git a/server/code.ts b/server/code.ts index 5ea5443..9b24b7b 100644 --- a/server/code.ts +++ b/server/code.ts @@ -91,7 +91,7 @@ if(debugTestBot) { testUserTestComment(); } -const port = environment == Environment.Development ? 3000 : 80; +const port = 80; const REFRESH_INTERVAL = environment == Environment.Development ? 180000 : 60000; const timingManager = new DataChangeTimingManager(); diff --git a/server/package-lock.json b/server/package-lock.json index a111786..a8caed4 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -35,7 +35,6 @@ "@typescript-eslint/parser": "^4.18.0", "eslint": "^7.22.0", "mocha": "^10.2.0", - "run-script-os": "^1.1.6", "ts-node": "^10.9.2", "tsc-watch": "^4.2.9", "typescript": "^5.8.3" @@ -556,6 +555,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz", "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -939,6 +939,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.2.2", @@ -1216,6 +1217,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -1237,6 +1239,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" }, @@ -1273,6 +1276,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", @@ -1746,6 +1750,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.30.0.tgz", "integrity": "sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=14" } @@ -2167,7 +2172,8 @@ "node_modules/@types/node": { "version": "16.7.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz", - "integrity": "sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==" + "integrity": "sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==", + "peer": true }, "node_modules/@types/parse-link-header": { "version": "1.0.0", @@ -2353,6 +2359,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.18.0.tgz", "integrity": "sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "4.18.0", "@typescript-eslint/types": "4.18.0", @@ -2467,6 +2474,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", "dev": true, + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3404,6 +3412,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz", "integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.0", @@ -4369,6 +4378,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6137,6 +6147,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", @@ -7082,16 +7093,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/run-script-os": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", - "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", - "dev": true, - "bin": { - "run-os": "index.js", - "run-script-os": "index.js" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -7772,6 +7773,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8565,6 +8567,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz", "integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==", + "peer": true, "requires": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -8885,6 +8888,7 @@ "version": "6.1.5", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.5.tgz", "integrity": "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==", + "peer": true, "requires": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.2.2", @@ -9114,7 +9118,8 @@ "@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==" + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "peer": true }, "@opentelemetry/api-logs": { "version": "0.57.2", @@ -9128,6 +9133,7 @@ "version": "1.30.1", "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.30.1.tgz", "integrity": "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==", + "peer": true, "requires": {} }, "@opentelemetry/core": { @@ -9149,6 +9155,7 @@ "version": "0.57.2", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz", "integrity": "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==", + "peer": true, "requires": { "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", @@ -9429,7 +9436,8 @@ "@opentelemetry/semantic-conventions": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.30.0.tgz", - "integrity": "sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw==" + "integrity": "sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw==", + "peer": true }, "@opentelemetry/sql-common": { "version": "0.40.1", @@ -9763,7 +9771,8 @@ "@types/node": { "version": "16.7.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz", - "integrity": "sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==" + "integrity": "sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==", + "peer": true }, "@types/parse-link-header": { "version": "1.0.0", @@ -9909,6 +9918,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.18.0.tgz", "integrity": "sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "4.18.0", "@typescript-eslint/types": "4.18.0", @@ -9970,7 +9980,8 @@ "version": "7.4.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.1", @@ -10641,6 +10652,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz", "integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==", "dev": true, + "peer": true, "requires": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.0", @@ -11348,7 +11360,8 @@ "acorn": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==" + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "peer": true }, "acorn-import-attributes": { "version": "1.9.5", @@ -12613,6 +12626,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "peer": true, "requires": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", @@ -13322,12 +13336,6 @@ "queue-microtask": "^1.2.2" } }, - "run-script-os": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", - "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", - "dev": true - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -13804,7 +13812,8 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true + "dev": true, + "peer": true }, "uglify-js": { "version": "3.19.3", diff --git a/server/package.json b/server/package.json index 13195d4..90d7442 100644 --- a/server/package.json +++ b/server/package.json @@ -4,12 +4,6 @@ "description": "Report on status of Keyman projects", "main": "dist/server/code.js", "type": "module", - "scripts": { - "build": "tsc", - "start": "run-script-os", - "start:windows": "_start_server.bat", - "start:default": "./_start_server.sh" - }, "author": "Marc Durdin", "license": "MIT", "dependencies": { @@ -39,7 +33,6 @@ "@typescript-eslint/parser": "^4.18.0", "eslint": "^7.22.0", "mocha": "^10.2.0", - "run-script-os": "^1.1.6", "ts-node": "^10.9.2", "tsc-watch": "^4.2.9", "typescript": "^5.8.3" diff --git a/server/tsconfig.json b/server/tsconfig.json index 731ac01..8e65caa 100644 --- a/server/tsconfig.json +++ b/server/tsconfig.json @@ -9,5 +9,8 @@ "allowJs": true, "skipLibCheck": true }, - "include": ["./**/*"] + "include": ["./**/*"], + "watchOptions": { + "watchFile": "dynamicPriorityPolling" + } } \ No newline at end of file From 16efc4db875c57c04cc18a43c1e1d6b2bbe55d4e Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Thu, 12 Feb 2026 10:50:56 +0100 Subject: [PATCH 2/5] chore: temp bootstrap version --- build.sh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index 0d177d6..ea8c16e 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,8 @@ ## START STANDARD SITE BUILD SCRIPT INCLUDE readonly THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" readonly BOOTSTRAP="$(dirname "$THIS_SCRIPT")/resources/bootstrap.inc.sh" -readonly BOOTSTRAP_VERSION=v1.0.9 +# TODO: release updated bootstrap version! +readonly BOOTSTRAP_VERSION=feat/docker-support-for-status-site [ -f "$BOOTSTRAP" ] && source "$BOOTSTRAP" || source <(curl -H "Cache-Control: no-cache" -fs https://raw.githubusercontent.com/keymanapp/shared-sites/$BOOTSTRAP_VERSION/bootstrap.inc.sh) ## END STANDARD SITE BUILD SCRIPT INCLUDE @@ -14,6 +15,9 @@ readonly BOOTSTRAP_VERSION=v1.0.9 # static on server side. # +source _common/keyman-local-ports.inc.sh +source _common/docker.inc.sh + readonly HOST_STATUS_KEYMAN_COM=status.keyman.com.localhost readonly THIS_CONTAINER_NAME=status-keyman-website @@ -27,8 +31,6 @@ readonly PUBLIC_HOST="$HOST_STATUS_KEYMAN_COM" # same host readonly THIS_PORT="$PORT_STATUS_KEYMAN_COM" readonly PUBLIC_PORT="$PORT_STATUS_KEYMAN_COM_PUBLIC" -source _common/keyman-local-ports.inc.sh -source _common/docker.inc.sh ################################ Main script ################################ From bdf0af1f9e8e4b29b82ec08552ea88455b3c9378 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 13 Feb 2026 05:10:51 +0100 Subject: [PATCH 3/5] chore: fixup azure deployment workflows for new repo layout For now, as we need to keep the deployment to Azure working, update the workflows to use the new script locations. Test first on staging. --- .../workflows/master_com-keyman-status.yml | 26 +++++++++++++++---- .../staging_com-keyman-staging-status.yml | 26 +++++++++++++++---- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/.github/workflows/master_com-keyman-status.yml b/.github/workflows/master_com-keyman-status.yml index 0665b8d..87fc9c2 100644 --- a/.github/workflows/master_com-keyman-status.yml +++ b/.github/workflows/master_com-keyman-status.yml @@ -19,13 +19,29 @@ jobs: - name: Set up Node.js version uses: actions/setup-node@v1 with: - node-version: '20.x' + node-version: '22.x' - - name: npm install, build, and test + - name: npm ci + build - public run: | - npm install - npm run build --if-present - npm run test --if-present + echo "- Building public for release" + cd public + npm ci + ./node_modules/.bin/ng build --configuration production + cd .. + + - name: npm ci + build - server + run: | + echo "- Building server for release" + cd server + npm ci + ./node_modules/.bin/tsc + cd .. + + - name: server test + run: | + cd server + npx mocha --import=./_mocha_register.js + cd .. - name: 'Deploy to Azure Web App' uses: azure/webapps-deploy@v2 diff --git a/.github/workflows/staging_com-keyman-staging-status.yml b/.github/workflows/staging_com-keyman-staging-status.yml index 35bbe10..c334d60 100644 --- a/.github/workflows/staging_com-keyman-staging-status.yml +++ b/.github/workflows/staging_com-keyman-staging-status.yml @@ -19,13 +19,29 @@ jobs: - name: Set up Node.js version uses: actions/setup-node@v1 with: - node-version: '20.x' + node-version: '22.x' - - name: npm install, build, and test + - name: npm ci + build - public run: | - npm install - npm run build --if-present - npm run test --if-present + echo "- Building public for release" + cd public + npm ci + ./node_modules/.bin/ng build --configuration production + cd .. + + - name: npm ci + build - server + run: | + echo "- Building server for release" + cd server + npm ci + ./node_modules/.bin/tsc + cd .. + + - name: server test + run: | + cd server + npx mocha --import=./_mocha_register.js + cd .. - name: 'Deploy to Azure Web App' uses: azure/webapps-deploy@v2 From 1b9cdd9940b2d6a8d35bd9c37fb38f43d21cc9d1 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 13 Feb 2026 06:22:30 +0100 Subject: [PATCH 4/5] chore: v1.0.10 shared sites --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index ea8c16e..837f5b6 100755 --- a/build.sh +++ b/build.sh @@ -3,7 +3,7 @@ readonly THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" readonly BOOTSTRAP="$(dirname "$THIS_SCRIPT")/resources/bootstrap.inc.sh" # TODO: release updated bootstrap version! -readonly BOOTSTRAP_VERSION=feat/docker-support-for-status-site +readonly BOOTSTRAP_VERSION=v1.0.10 [ -f "$BOOTSTRAP" ] && source "$BOOTSTRAP" || source <(curl -H "Cache-Control: no-cache" -fs https://raw.githubusercontent.com/keymanapp/shared-sites/$BOOTSTRAP_VERSION/bootstrap.inc.sh) ## END STANDARD SITE BUILD SCRIPT INCLUDE From b85a98e3109ecb4de42b7ed1e399de03b59b3427 Mon Sep 17 00:00:00 2001 From: Marc Durdin Date: Fri, 13 Feb 2026 08:00:30 +0100 Subject: [PATCH 5/5] chore: address review comments, add docs, and cleanup Note that the changes to build.sh prologue should be replicated across our other sites to give additional stability. Co-authored-by: Eberhard Beilharz --- .gitignore | 2 +- README.md | 85 +++++++++++++++++++++++++++++---------- build.sh | 15 +++---- resources/start-server.sh | 6 +-- resources/test.sh | 2 +- 5 files changed, 73 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index 5b7278f..68b6172 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ node_modules/ local*.sh # Shared files are bootstrapped: -resources/bootstrap.inc.sh +resources/bootstrap.inc.sh* resources/.bootstrap-version resources/.bootstrap-registry _common/ diff --git a/README.md b/README.md index cc3f0e8..e730b53 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,23 @@ -# Quick Start +# status.keyman.com -Clone the repo: +## Overview -```bash -git clone https://github.com/keymanapp/status.keyman.com -cd status.keyman.com/ -``` +* Back end is a node server in `/server`. +* Front end is an Angular app in `/public`. +* The [Keyman Test-bot](https://github.com/keymanapp/keyman/wiki/User-Testing-Workflows) + (@keymanapp-test-bot) is in `/server/keymanapp-test-bot`. + +The site is setup to run in Docker container(s). + +The site can be setup in development (`--debug`) or production (`--release`) +modes. When in development mode, the front end is hosted (on +http://localhost:8061) in a separate container to the back end (on +http://localhost:8060) in order to facilitate live-reload on changes. -### Prerequisites +For production mode, the front end is compiled to static pages which are then +served by the back end (on http://localhost:8060). + +## Prerequisites * Docker Desktop * On Windows, you'll need to have Git Bash installed in `C:\Program Files\git\bin\bash.exe`. @@ -21,7 +31,24 @@ export KEYMANSTATUS_GITHUB_TOKEN=[your personal auth token here] export KEYMANSTATUS_SENTRY_TOKEN=[your personal auth token here] ``` -## Build the docker containers +### @keymanapp-test-bot + +Three files are needed for development: + +* `.keymanapp-test-bot.appid`: integer appid (e.g. 134443 for the normal test app) +* `.keymanapp-test-bot.pem`: certificate for GitHub integration for app +* `.keymanapp-test-bot.secret`: secret for GitHub integration for app + +## Development setup + +Clone the repo: + +```bash +git clone https://github.com/keymanapp/status.keyman.com +cd status.keyman.com/ +``` + +### Build the docker containers Build status.keyman.com, in development mode: @@ -29,7 +56,7 @@ Build status.keyman.com, in development mode: ./build.sh stop build --debug ``` -## Start the development server +### Start the development server This repo is configured for live build and reload of both the client and server, running in Docker. @@ -40,6 +67,33 @@ running in Docker. * Point your browser to to view the live reload version of the application. +* The site takes a moment to compile and load; you can watch the logs to see + when it is ready. + +### Running unit tests + +The unit tests will currently stop the back end container before running. + +```bash +./build.sh test --debug +``` + +## Production setup + +This site is deployed to a Kubernetes cluster via configuration in a private +repo to status.keyman.com. + +You can run the production mode site locally with: + +```bash +./build.sh stop build start --release +``` + +* Point your browser to to view the production version + of the application. + +## Site query parameters + * The following query parameters are available: * `?c=1` shows contributions at the top center * `?o=1` shows owner for each platform @@ -48,16 +102,3 @@ running in Docker. costly, so only press this when there has been a data error such as a network failure making status data out of date; most errors are actually self-healing) - -### @keymanapp-test-bot - -Three files needed for development: - -* `.keymanapp-test-bot.appid`: integer appid (e.g. 134443 for the normal test app) -* `.keymanapp-test-bot.pem`: certificate for GitHub integration for app -* `.keymanapp-test-bot.secret`: secret for GitHub integration for app - -## Production - -This site is deployed to a Kubernetes cluster via configuration in a private -repo to status.keyman.com. diff --git a/build.sh b/build.sh index 837f5b6..7b449dc 100755 --- a/build.sh +++ b/build.sh @@ -2,19 +2,14 @@ ## START STANDARD SITE BUILD SCRIPT INCLUDE readonly THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")" readonly BOOTSTRAP="$(dirname "$THIS_SCRIPT")/resources/bootstrap.inc.sh" -# TODO: release updated bootstrap version! readonly BOOTSTRAP_VERSION=v1.0.10 -[ -f "$BOOTSTRAP" ] && source "$BOOTSTRAP" || source <(curl -H "Cache-Control: no-cache" -fs https://raw.githubusercontent.com/keymanapp/shared-sites/$BOOTSTRAP_VERSION/bootstrap.inc.sh) +if ! [ -f "$BOOTSTRAP" ] || ! source "$BOOTSTRAP"; then + curl -H "Cache-Control: no-cache" --fail --silent --show-error -w "curl: Finished attempt to download %{url}" "https://raw.githubusercontent.com/keymanapp/shared-sites/$BOOTSTRAP_VERSION/bootstrap.inc.sh" -o "$BOOTSTRAP.tmp" || exit 1 + source "$BOOTSTRAP.tmp" + rm -f "$BOOTSTRAP.tmp" +fi ## END STANDARD SITE BUILD SCRIPT INCLUDE -# -# in development mode, the public host is on a separate port to reduce the -# complexity of tsc --watch vs nodemon; in the future we could consider -# consolidation into a single container. There is little benefit into splitting -# production into two containers, given production version of public is fully -# static on server side. -# - source _common/keyman-local-ports.inc.sh source _common/docker.inc.sh diff --git a/resources/start-server.sh b/resources/start-server.sh index c8484ab..24ff6e9 100755 --- a/resources/start-server.sh +++ b/resources/start-server.sh @@ -18,13 +18,13 @@ if [ "$BUILDER_CONFIGURATION" = release ]; then # same container echo "- Building public for release" cd public - npm install + npm ci ./node_modules/.bin/ng build --configuration production cd .. echo "- Building server for release" cd server - npm install + npm ci ./node_modules/.bin/tsc cd .. else @@ -33,7 +33,7 @@ else # done with tsc-watch echo "- Preparing server for debug" cd server - npm install + npm ci cd .. fi diff --git a/resources/test.sh b/resources/test.sh index bdd3fd8..537d16c 100755 --- a/resources/test.sh +++ b/resources/test.sh @@ -13,5 +13,5 @@ echo "Testing server with $BUILDER_CONFIGURATION configuration" cd server -npm install +npm ci npx mocha --import=./_mocha_register.js \ No newline at end of file