Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4b244f7
Merge pull request #571 from IATI/main
simon-20 May 21, 2025
0b7311c
Github access - switch to Oauth app credentials. And reduce API calls.
jarofgreen May 28, 2025
6b878f7
Merge pull request #572 from IATI/2025-05-28
jarofgreen May 28, 2025
98b6c15
docs: correct dev service hostnames and clarify
arobson-ods Aug 17, 2026
20ccfbb
ci: refuse commits that set authLevel to anonymous
arobson-ods Aug 17, 2026
c497165
test: add failing schema-validation tests
arobson-ods Aug 18, 2026
40de213
style: fix eslint errors on the github api helpers
arobson-ods Aug 19, 2026
2bc624c
fix: replace libxmljs2 with libxml2-wasm to catch missed schema errors
arobson-ods Aug 19, 2026
28bfa14
chore: rename rules:test to unit:test
arobson-ods Aug 24, 2026
bfbe072
chore: bump version to 2.5.0
arobson-ods Aug 24, 2026
0ba5cca
chore: add a CHANGELOG
arobson-ods Aug 24, 2026
24ac0ac
Merge pull request #575 from IATI/ar/upgrade-libxml
arobson-ods Aug 24, 2026
84b5d91
build(deps): bump fastify/github-action-merge-dependabot (#576)
dependabot[bot] Aug 24, 2026
f328efe
build(deps): bump azure/CLI from 1.0.8 to 3.0.0 (#577)
dependabot[bot] Aug 24, 2026
b40c4c3
build(deps): bump azure/login from 1.4.7 to 3.0.1 (#580)
dependabot[bot] Aug 24, 2026
0930018
build(deps): bump actions/checkout from 4 to 7 (#579)
dependabot[bot] Aug 24, 2026
e3bd07f
build(deps): bump actions/setup-node from 4 to 7 (#578)
dependabot[bot] Aug 24, 2026
bc95e46
chore: sync package-lock.json version with package.json
arobson-ods Aug 24, 2026
ffd6384
build(deps): bump azure/docker-login from 1 to 2 (#582)
dependabot[bot] Aug 24, 2026
28b0d1e
Merge branch 'develop' into ar/sync-lockfile-version
arobson-ods Aug 24, 2026
85fdcd6
Merge pull request #581 from IATI/ar/sync-lockfile-version
arobson-ods Aug 25, 2026
7495079
docs(deploy): document why workflows get disabled
simon-20 Aug 25, 2026
fa37404
Merge pull request #583 from IATI/sk/update-docs
simon-20 Aug 25, 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
11 changes: 7 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
NODE_ENV=development

APPLICATIONINSIGHTS_CONNECTION_STRING=
APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=00000000-0000-0000-0000-000000000000;IngestionEndpoint=https://localhost/

BASIC_GITHUB_TOKEN=
# Optional, but without it GitHub rate limiting will usually stop the rulesets
# and codelists downloading at startup.
GITHUB_OAUTH_APP_CLIENT_ID=
GITHUB_OAUTH_APP_CLIENT_SECRET=

REDIS_PORT=6379
REDIS_HOSTNAME=redis # replace with localhost if you run outside docker

VALIDATOR_SERVICES_URL=https://dev-func-validator-services.azurewebsites.net/api
VALIDATOR_SERVICES_URL=https://func-validator-services-dev.azurewebsites.net/api
VALIDATOR_SERVICES_KEY_NAME=x-functions-key
VALIDATOR_SERVICES_KEY_VALUE=

DATASTORE_SERVICES_URL=https://dev-func-datastore-services.azurewebsites.net/api
DATASTORE_SERVICES_URL=https://func-datastore-services-dev.azurewebsites.net/api
DATASTORE_SERVICES_AUTH_HTTP_HEADER_NAME=x-functions-key
DATASTORE_SERVICES_AUTH_HTTP_HEADER_VALUE=
DATASTORE_SERVICES_IATI_IDENTIFIERS_EXIST_MAX_NUMBER_OF_IDS=5000
9 changes: 9 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ updates:
directory: '/'
schedule:
interval: 'daily'
ignore:
# Pinned exactly, and upgraded by hand. A libxml2-wasm release can carry a different
# bundled libxml2, which is what decides whether a file is schema valid - and since
# v0.7.0 its libxml2 submodule tracks the maintainer's fork rather than an upstream
# release tag, so a bump also takes whatever else is on that branch. PR CI does not
# run the differential harness that verified verdicts against the Dashboard, and
# dependabot PRs auto-merge once CI is green, so a bump would otherwise land with no
# human reading it. See README "XML Library".
- dependency-name: 'libxml2-wasm'

# Enable version updates for GitHub Actions
- package-ecosystem: 'github-actions'
Expand Down
43 changes: 37 additions & 6 deletions .github/workflows/develop-func-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ on:
- main

jobs:
# Running the integration tests against the Docker container requires temporarily
# setting "authLevel": "anonymous" in the function.json files (see README), because
# the container has no AzureWebJobsStorage and so cannot validate function keys.
# Committing that would leave the deployed endpoints reachable without a key.
# There is a matching .husky/pre-commit guard, but hooks are skippable with
# --no-verify and are absent in a fresh clone until `npm install` has run.
check-auth-level:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v7

- name: 'Reject authLevel anonymous in function.json'
run: |
if grep -il '"authLevel"[[:space:]]*:[[:space:]]*"anonymous"' */function.json > /tmp/hits; then
echo "::error::function.json committed with authLevel anonymous - the deployed endpoint would need no function key"
echo "Offending files:"
cat /tmp/hits
echo ""
echo "Restore with: git checkout -- '*/function.json'"
exit 1
fi
echo "OK - all function.json files require a function key."

build-test-node:
runs-on: ubuntu-latest
steps:
Expand All @@ -21,10 +45,10 @@ jobs:
sudo apt-get install libxml2-utils

- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: 'Setup Node Environment'
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-version-file: '.nvmrc'
cache: 'npm'
Expand All @@ -34,26 +58,33 @@ jobs:
npm i -g npm@^10
npm ci

# Runs before the unit tests because it catches a class they cannot: a source file
# importing a package that is not installed. No unit test imports utils.js - it
# pulls in Redis - so a tree where utils.js and package.json disagree about the XML
# library still reports every test passing.
- name: 'Lint'
run: npx eslint .

- name: 'Run unit tests'
run: npm run rules:test
run: npm run unit:test

build-docker:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: 'Build Docker Image'
run: |
docker build . -t validator-local:latest

automerge:
needs: [build-test-node, build-docker]
needs: [check-auth-level, build-test-node, build-docker]
runs-on: ubuntu-latest
permissions:
pull-requests: write
contents: write
steps:
- uses: fastify/github-action-merge-dependabot@v3.9.1
- uses: fastify/github-action-merge-dependabot@v3.15.0
with:
github-token: ${{secrets.GITHUB_TOKEN}}
22 changes: 14 additions & 8 deletions .github/workflows/develop-func-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ env:
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
KV_URI: ${{ secrets.DEV_KV_URI }}
BASIC_GITHUB_TOKEN: ${{ secrets.BASIC_GITHUB_TOKEN }}
GITHUB_OAUTH_APP_CLIENT_ID: ${{ secrets.IATI_GITHUB_OAUTH_APP_CLIENT_ID }}
GITHUB_OAUTH_APP_CLIENT_SECRET: ${{ secrets.IATI_GITHUB_OAUTH_APP_CLIENT_SECRET }}
DATASTORE_SERVICES_URL: ${{ secrets.DEV_DATASTORE_SERVICES_URL }}
DATASTORE_SERVICES_AUTH_HTTP_HEADER_NAME: ${{ secrets.DEV_DATASTORE_SERVICES_AUTH_HTTP_HEADER_NAME }}
DATASTORE_SERVICES_AUTH_HTTP_HEADER_VALUE: ${{ secrets.DEV_DATASTORE_SERVICES_AUTH_HTTP_HEADER_VALUE }}
Expand All @@ -48,15 +49,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: 'Login via Azure CLI'
uses: azure/login@v1.4.7
uses: azure/login@v3.0.1
with:
creds: ${{ env.AZURE_CREDENTIALS }} # Service Principal credentials saved in GitHub Secrets

- name: 'Docker Login'
uses: azure/docker-login@v1
uses: azure/docker-login@v2
with:
login-server: ${{ env.ACR_LOGIN_SERVER }}
username: ${{ env.ACR_USERNAME }}
Expand Down Expand Up @@ -134,8 +135,13 @@ jobs:
"slotSetting": false
},
{
"name": "BASIC_GITHUB_TOKEN",
"value": "${{ env.BASIC_GITHUB_TOKEN }}",
"name": "GITHUB_OAUTH_APP_CLIENT_ID",
"value": "${{ env.GITHUB_OAUTH_APP_CLIENT_ID }}",
"slotSetting": false
},
{
"name": "GITHUB_OAUTH_APP_CLIENT_SECRET",
"value": "${{ env.GITHUB_OAUTH_APP_CLIENT_SECRET }}",
"slotSetting": false
},
{
Expand Down Expand Up @@ -172,7 +178,7 @@ jobs:
image: ${{ env.ACR_LOGIN_SERVER }}/func-${{ env.NAME}}-${{ env.STAGE }}:${{ env.TAG }}

- name: 'Tag resource for Tech Climate Impact'
uses: 'azure/CLI@v1.0.8'
uses: 'azure/CLI@v3.0.0'
with:
azcliversion: 2.30.0
inlineScript: |
Expand All @@ -190,7 +196,7 @@ jobs:
run: sleep 180

- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: 'Install newman'
run: |
Expand Down
22 changes: 14 additions & 8 deletions .github/workflows/prod-func-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ env:
ACR_USERNAME: ${{ secrets.ACR_USERNAME }}
ACR_PASSWORD: ${{ secrets.ACR_PASSWORD }}
KV_URI: ${{ secrets.PROD_KV_URI }}
BASIC_GITHUB_TOKEN: ${{ secrets.BASIC_GITHUB_TOKEN }}
GITHUB_OAUTH_APP_CLIENT_ID: ${{ secrets.IATI_GITHUB_OAUTH_APP_CLIENT_ID }}
GITHUB_OAUTH_APP_CLIENT_SECRET: ${{ secrets.IATI_GITHUB_OAUTH_APP_CLIENT_SECRET }}
DATASTORE_SERVICES_URL: ${{ secrets.PROD_DATASTORE_SERVICES_URL }}
DATASTORE_SERVICES_AUTH_HTTP_HEADER_NAME: ${{ secrets.PROD_DATASTORE_SERVICES_AUTH_HTTP_HEADER_NAME }}
DATASTORE_SERVICES_AUTH_HTTP_HEADER_VALUE: ${{ secrets.PROD_DATASTORE_SERVICES_AUTH_HTTP_HEADER_VALUE }}
Expand All @@ -33,15 +34,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: 'Login via Azure CLI'
uses: azure/login@v1.4.7
uses: azure/login@v3.0.1
with:
creds: ${{ env.AZURE_CREDENTIALS }} # Service Principal credentials saved in GitHub Secrets

- name: 'Docker Login'
uses: azure/docker-login@v1
uses: azure/docker-login@v2
with:
login-server: ${{ env.ACR_LOGIN_SERVER }}
username: ${{ env.ACR_USERNAME }}
Expand Down Expand Up @@ -119,8 +120,13 @@ jobs:
"slotSetting": false
},
{
"name": "BASIC_GITHUB_TOKEN",
"value": "${{ env.BASIC_GITHUB_TOKEN }}",
"name": "GITHUB_OAUTH_APP_CLIENT_ID",
"value": "${{ env.GITHUB_OAUTH_APP_CLIENT_ID }}",
"slotSetting": false
},
{
"name": "GITHUB_OAUTH_APP_CLIENT_SECRET",
"value": "${{ env.GITHUB_OAUTH_APP_CLIENT_SECRET }}",
"slotSetting": false
},
{
Expand Down Expand Up @@ -157,7 +163,7 @@ jobs:
image: ${{ env.ACR_LOGIN_SERVER }}/func-${{ env.NAME}}-${{ env.STAGE }}:${{ env.TAG }}

- name: 'Tag resource for Tech Climate Impact'
uses: 'azure/CLI@v1.0.8'
uses: 'azure/CLI@v3.0.0'
with:
azcliversion: 2.30.0
inlineScript: |
Expand All @@ -175,7 +181,7 @@ jobs:
run: sleep 300

- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: 'Install newman'
run: |
Expand Down
25 changes: 25 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

# Running the integration tests against the Docker container requires temporarily
# setting "authLevel": "anonymous" in the function.json files (see README). The
# container has no AzureWebJobsStorage, so it cannot validate function keys and
# every request 401s otherwise.
#
# Committing that change would leave the deployed endpoints reachable without a
# function key. This guard refuses the commit instead of relying on memory.
#
# Restore with: git checkout -- '*/function.json'
for f in $(git diff --cached --name-only --diff-filter=ACM | grep 'function\.json$'); do
if git show ":$f" | grep -qi '"authLevel"[[:space:]]*:[[:space:]]*"anonymous"'; then
echo ""
echo "ERROR $f is staged with \"authLevel\": \"anonymous\""
echo ""
echo " That would leave the deployed endpoint unauthenticated."
echo " It is only meant to be set temporarily, to run the"
echo " integration tests against the Docker container."
echo ""
echo " Restore all of them with:"
echo " git checkout -- '*/function.json'"
echo ""
exit 1
fi
done

npx --no-install lint-staged
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# AGENTS.md

Dev deployments are often deferred rather than immediate; if a push to `develop` did not deploy, see the "Deploying to dev" section of README.md before investigating.
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Added

### Changed

### Fixed

### Removed

## [2.5.0] - 2026-08-24

### Changed

- Replaced libxmljs2 with libxml2-wasm, moving from libxml2 2.9.9 (2019) to
2.15.1. libxmljs2 is no longer maintained and no release of it carries a newer
libxml2, so the library was replaced rather than upgraded.
- **`?details=true` responses have changed shape.** Error details are now
`{ line, column }`, where they were the raw libxml2 error struct. See Removed.
- Schemas are now compiled once at startup rather than re-parsed for every
activity, so validation of files with schema errors should be faster.

### Added

- Schema unit tests (`schema-unit-tests`) and tests for file parsing and
metadata (`utils-unit-tests`). Neither area had any coverage before.
- A pre-commit hook and CI check that refuse a commit setting
`"authLevel": "anonymous"` in any `function.json`. That value is only meant to
be set temporarily, to run the integration tests against the Docker container.

### Fixed

- Schema validation: sign-only decimals such as `<value> - </value>` are now
reported; over-long decimals no longer are. This is the divergence with the
IATI Dashboard reported in
[#574](https://github.com/IATI/js-validator-api/issues/574).
- `.env.example`: corrected the dev service hostnames

### Removed

- `libxmljs2`, and with it the native build dependency — no node-gyp,
prebuilt binaries or glibc coupling.
- `str1`, `code`, `level`, `domain` and `int1` from the `details` object
returned by `?details=true`. `str1` still appears within the error message;
`level` and `domain` were constant; `str2`, `str3` and `int1` were never
populated. `code`, the numeric libxml2 constraint identifier, has no
replacement — libxml2-wasm does not expose it.
Loading
Loading