Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
6d19d41
OI-32 Revising security, make sure there is no leak of data in error …
sedv8808 Aug 11, 2026
57d2388
OI-32 Revising security, make sure there is no leak of data in error …
sedv8808 Aug 11, 2026
e6b0cbf
TA-11 Modifying session usage in DB so that when a person logs in aga…
sedv8808 Oct 14, 2026
4151546
TA-11 Update test folder in NODE API as well as package.json and .git…
sedv8808 Oct 14, 2026
26a956f
NLP-30 Fixing landing pages for issue Alison reported: The /v2.0/apps…
sedv8808 Oct 21, 2026
c7e4604
Send a single response from taxonquery so a failed query no longer c…
sedv8808 Oct 21, 2026
4a98b1d
Log unhandled rejections and uncaught exceptions before exiting
sedv8808 Oct 21, 2026
0191e53
Send a single response from taxonquery so a failed query no longer c…
sedv8808 Oct 21, 2026
d602754
Make nodemon exit on crash so crashed production instances restart
sedv8808 Oct 21, 2026
dd22521
Restore v2.0-data-taxa-test.js to the production version
sedv8808 Oct 21, 2026
841d25c
Merge pull request #238 from NeotomaDB/hotfix/taxa-crash
sedv8808 Oct 21, 2026
ef23d98
Make node 22 the right one accross scripts
sedv8808 Oct 21, 2026
dea6ecd
Error responses in the v2.0 handlers put the database error in data,
sedv8808 Oct 21, 2026
369f944
script was not properly cleaning old files
sedv8808 Oct 21, 2026
6358077
Test only the server that was just deployed
sedv8808 Oct 21, 2026
dbd57c2
Run build and startup checks on pushes as well as pull requests
sedv8808 Oct 21, 2026
468eecf
Best labeling of checks
sedv8808 Oct 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 96 additions & 11 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,15 @@ on:
workflow_run:
workflows: ["Deploy Neotoma API"]
types: [completed]
branches: [develop]
branches: [develop, production]
# The `checks` job runs on every pull request into develop or production (so
# develop -> production too) and on every push to those branches. The `test`
# job does not: it tests a deployed server, and neither a pull request nor a
# push that has not been deployed yet is running anywhere.
pull_request:
branches: [develop, production]
push:
branches: [develop, production]
workflow_dispatch:

jobs:
Expand All @@ -13,31 +21,44 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
timeout-minutes: 25

# Test only the server that was just deployed: a push to `production` tests
# production, anything else (a push to `develop`, or a manual run started
# from another branch) tests dev. After a deploy the branch is the one that
# triggered the Deploy run; for a manual run it is the branch it started on.
strategy:
fail-fast: false
matrix:
include:
- name: dev
flag: '-d'
host: https://api-dev.neotomadb.org
- name: production
flag: '-p'
host: https://api.neotomadb.org
include: ${{ fromJSON((github.event.workflow_run.head_branch || github.ref_name) == 'production' && '[{"name":"production","flag":"-p","host":"https://api.neotomadb.org"}]' || '[{"name":"dev","flag":"-d","host":"https://api-dev.neotomadb.org"}]') }}

name: test (${{ matrix.name }})
# A fixed name: when this job is skipped (push and pull_request runs) GitHub
# never expands the matrix, so a name that used matrix.name showed up as the
# raw "${{ matrix.name }}" text. The target (dev or production) is in the
# step names and the test-report artifact name instead.
name: Test deployed server

steps:
- name: Checkout code
uses: actions/checkout@v5
with:
# A workflow_run job checks out the default branch unless told
# otherwise. Check out the commit that was deployed so its own tests
# and openapi.yaml are what runs.
ref: ${{ github.event.workflow_run.head_sha || github.sha }}

# package.json pins "packageManager": "yarn@4.12.0". The runner's global
# Yarn is 1.22.x, which refuses such a project, so Corepack has to be on
# before setup-node runs `yarn` for its cache.
- name: Enable Corepack
run: corepack enable

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '22' # keep in step with FROM node:22-alpine in the Dockerfile
cache: 'yarn'

- name: Install dependencies
run: yarn install --frozen-lockfile
run: yarn install --immutable

# genoatt.sh and runmochabatch.sh invoke `oatts` and `mocha` bare. Those resolve
# under `yarn run` but not under `run: bash …`, so put the bin dir on PATH.
Expand Down Expand Up @@ -73,3 +94,67 @@ jobs:
with:
name: test-report-${{ matrix.name }}
path: public/tests.html

# Checks that need no database, so they test the pushed or proposed code on
# the runner. The mocha suite needs a live API and database, so it only runs
# after a deploy (the `test` job above).
checks:
name: Checks (syntax, OpenAPI spec, startup)
if: ${{ github.event_name == 'pull_request' || github.event_name == 'push' }}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
concurrency:
# A newer push to a pull request replaces that pull request's older run.
# Every push to develop/production gets its own group (the run id), so
# nothing is cancelled and every commit has a result.
group: checks-${{ github.event_name == 'pull_request' && github.ref || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

steps:
- name: Checkout code
uses: actions/checkout@v5

- name: Enable Corepack
run: corepack enable

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22' # keep in step with FROM node:22-alpine in the Dockerfile
cache: 'yarn'

- name: Install dependencies
run: yarn install --immutable

- name: Check JavaScript syntax
run: find app.js database src v1.5 v2.0 openapi/scripts -name '*.js' -print0 | xargs -0 -n1 node --check

- name: Build the OpenAPI spec and check openapi.yaml is up to date
run: |
yarn build:openapi
if ! git diff --exit-code -- openapi.yaml; then
echo "::error file=openapi.yaml::openapi.yaml is out of date. Run 'yarn build:openapi' and commit the result."
exit 1
fi

- name: Start the API without a database and check /healthcheck
env:
APIPORT: '3999'
NODE_ENV: development
run: |
node app.js > app.log 2>&1 &
pid=$!
for i in $(seq 1 30); do
if curl -sf http://127.0.0.1:3999/healthcheck/ > /dev/null; then
echo "healthcheck answered after ${i}s"
kill "$pid"
exit 0
fi
sleep 1
done
echo "::error::The API did not answer /healthcheck/ within 30 s"
cat app.log
kill "$pid" 2>/dev/null || true
exit 1
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,7 @@ build_deploy.sh

output.txt

docs/
test/unit/
.yarn/
tasks/
12 changes: 12 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,16 @@ app.listen(apiPort, () => {
console.log(`Neotoma API listening on port ${apiPort} (NODE_ENV=${process.env.NODE_ENV})`);
});

// Log the reason before the process dies. Node exits on these, and under
// `node app.js` (no nodemon) App Runner then restarts the container promptly.
// Message and stack only: error objects from pg can carry query parameters.
process.on('unhandledRejection', (reason) => {
console.error('unhandledRejection:', reason && reason.stack ? reason.stack : String(reason));
process.exit(1);
});
process.on('uncaughtException', (err) => {
console.error('uncaughtException:', err && err.stack ? err.stack : String(err));
process.exit(1);
});

module.exports = app;
5 changes: 5 additions & 0 deletions openapi/scripts/build-openapi.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ function buildOpenAPI() {
function findYamlElements(dir) {
// self note: you can `push()` to a const array.
const yamlElements = [];
// Git does not keep empty folders, so a section with no files yet (for
// example components/responses) is absent from a fresh checkout.
if (!fs.existsSync(dir)) {
return yamlElements;
}
const items = fs.readdirSync(dir);
items.forEach((item) => {
const fullPath = path.join(dir, item);
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
"license": "MIT",
"private": true,
"scripts": {
"start": "NODE_ENV=production nodemon --legacy-watch app.js",
"start": "NODE_ENV=production nodemon --legacy-watch --exitcrash app.js",
"dev": "NODE_ENV=development nodemon --legacy-watch app.js",
"test": "APIPATH=${APIPATH:-http://localhost:3001/} node ./node_modules/mocha/bin/mocha --config=test/.mocharc.yml --reporter-options reportDir=public,reportFilename=tests",
"test:unit": "node ./node_modules/mocha/bin/mocha --no-config --recursive --reporter spec --timeout 10000 test/unit",
"build:openapi": "(cd ./openapi && node ./scripts/build-openapi.js)",
"validate:openapi": "yarn build:openapi && bash genoatt.sh && sleep 4 && bash runmochabatch.sh"
},
Expand Down
2 changes: 1 addition & 1 deletion public/tests.html

Large diffs are not rendered by default.

Loading
Loading