Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
89 changes: 89 additions & 0 deletions .github/workflows/contract-reproducibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Contract Build Reproducibility

on:
pull_request:
paths:
- 'contract/**'
- 'scripts/reproducible-contract-build.sh'
- '.github/workflows/contract-reproducibility.yml'
- 'docs/CONTRACT_BUILD_REPRODUCIBILITY.md'
push:
branches: [main]
paths:
- 'contract/**'
- 'scripts/reproducible-contract-build.sh'
- '.github/workflows/contract-reproducibility.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
build:
name: Clean contract build (${{ matrix.build }})
runs-on: ubuntu-24.04
timeout-minutes: 25
strategy:
fail-fast: false
matrix:
build: [first, second]
steps:
- uses: actions/checkout@v4
- name: Install pinned Rust toolchain and target
shell: bash
run: |
mkdir -p reproducibility-output
toolchain=$(sed -n 's/^channel = "\([^"]*\)"$/\1/p' contract/rust-toolchain.toml)
test -n "$toolchain"
rustup toolchain install "$toolchain" --profile minimal --target wasm32v1-none 2>&1 | tee reproducibility-output/toolchain.log
- name: Build committed sources without shared caches
run: bash scripts/reproducible-contract-build.sh '${{ matrix.build }}' "$GITHUB_WORKSPACE/reproducibility-output"
- name: Upload build outputs and diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: contract-repro-${{ matrix.build }}
path: reproducibility-output/
if-no-files-found: warn
retention-days: 14

compare:
name: Compare contract Wasm bytes
needs: build
if: always()
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: Download build diagnostics
uses: actions/download-artifact@v4
with:
pattern: contract-repro-*
path: builds
- name: Compare and report
if: always()
shell: bash
run: |
mkdir -p comparison
first=builds/contract-repro-first/hello_world.wasm
second=builds/contract-repro-second/hello_world.wasm
if [[ ! -f "$first" || ! -f "$second" ]]; then
echo 'Reproducibility could not be verified: one or both builds did not produce Wasm. Inspect the build logs.' | tee comparison/result.txt >> "$GITHUB_STEP_SUMMARY"
exit 1
fi
sha256sum "$first" "$second" | tee comparison/SHA256SUMS >> "$GITHUB_STEP_SUMMARY"
if cmp -s "$first" "$second"; then
echo 'PASS: clean builds produced byte-identical release Wasm.' | tee comparison/result.txt >> "$GITHUB_STEP_SUMMARY"
else
echo 'FAIL: release Wasm differs. Inspect both environment.txt files, build logs, and byte-differences.txt.' | tee comparison/result.txt >> "$GITHUB_STEP_SUMMARY"
# cmp returns 1 for differences; retain only the first 100 offsets.
(cmp -l "$first" "$second" | head -n 100) > comparison/byte-differences.txt || true
exit 1
fi
- name: Upload comparison diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: contract-repro-comparison
path: comparison/
if-no-files-found: warn
retention-days: 14
52 changes: 52 additions & 0 deletions .github/workflows/frontend-accessibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Frontend accessibility

on:
pull_request:
paths:
- 'dashboard/**'
- '.github/workflows/frontend-accessibility.yml'
- 'docs/accessibility-automation.md'
push:
branches: [main, master]
paths:
- 'dashboard/**'
- '.github/workflows/frontend-accessibility.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
accessibility:
runs-on: ubuntu-latest
timeout-minutes: 10
defaults:
run:
working-directory: dashboard
shell: bash
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: dashboard/package-lock.json
- run: npm ci
- name: Check shared components and representative page flows
run: |
mkdir -p accessibility-results
set -o pipefail
npm run test:a11y -- --ci --verbose --json --outputFile=accessibility-results/jest.json 2>&1 | tee accessibility-results/jest.txt
- name: Preserve accessibility reports
if: always()
uses: actions/upload-artifact@v4
with:
name: frontend-accessibility-results
path: dashboard/accessibility-results/
if-no-files-found: warn
retention-days: 14
- name: Record review scope
if: always()
run: |
echo '### Frontend accessibility' >> "$GITHUB_STEP_SUMMARY"
echo 'See the test log and frontend-accessibility-results artifact for rule IDs, affected elements, and remediation links. Automated DOM checks do not replace keyboard, screen-reader, zoom, contrast, or mobile browser review. See docs/accessibility-automation.md.' >> "$GITHUB_STEP_SUMMARY"
47 changes: 47 additions & 0 deletions .github/workflows/listener-mutation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Listener mutation testing

on:
pull_request:
paths:
- 'listener/**'
- '.github/workflows/listener-mutation.yml'
push:
branches: [main]
paths:
- 'listener/**'
- '.github/workflows/listener-mutation.yml'
workflow_dispatch:

permissions:
contents: read

jobs:
critical-events:
runs-on: ubuntu-latest
timeout-minutes: 30
defaults:
run:
working-directory: listener
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22.22.0'
cache: npm
cache-dependency-path: listener/package-lock.json
- run: npm ci
- name: Mutate critical event processing
run: node scripts/mutation/run.cjs
- name: Publish mutation summary
if: always()
run: |
if [ -f reports/mutation/summary.md ]; then
cat reports/mutation/summary.md >> "$GITHUB_STEP_SUMMARY"
fi
- uses: actions/upload-artifact@v4
if: always()
with:
name: critical-event-mutations
path: listener/reports/mutation/
if-no-files-found: warn
retention-days: 14
63 changes: 63 additions & 0 deletions .github/workflows/listener-node-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Listener Node.js compatibility

on:
pull_request:
paths: ['listener/**', '.github/workflows/listener-node-compatibility.yml']
push:
branches: [main]
paths: ['listener/**', '.github/workflows/listener-node-compatibility.yml']
workflow_dispatch:

permissions:
contents: read

jobs:
compatibility:
name: Listener / Node ${{ matrix.node }}
runs-on: ubuntu-24.04
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
node: ['22', '24']
defaults:
run:
working-directory: listener
shell: bash
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: npm
cache-dependency-path: listener/package-lock.json
- name: Record runtime and dependency installation
id: install
run: |
set -o pipefail
mkdir -p reports/node-compatibility
node --version | tee reports/node-compatibility/node.txt
npm --version | tee reports/node-compatibility/npm.txt
npm ci 2>&1 | tee reports/node-compatibility/install.log
- name: Build listener
id: build
run: |
set -o pipefail
npm run build 2>&1 | tee reports/node-compatibility/build.log
- name: Run notification processing tests
if: ${{ !cancelled() && steps.install.outcome == 'success' }}
run: |
set -o pipefail
npm test -- --ci --runInBand src/__tests__/smoke/notification-pipeline.smoke.test.ts src/services/event-normalization.test.ts src/services/notification-claim-lease.test.ts 2>&1 | tee reports/node-compatibility/tests.log
- name: Exercise emitted JavaScript and real runtime dependencies
if: ${{ !cancelled() && steps.build.outcome == 'success' }}
run: |
set -o pipefail
npm run test:runtime 2>&1 | tee reports/node-compatibility/runtime.log
- name: Upload runtime-specific diagnostics
if: always()
uses: actions/upload-artifact@v4
with:
name: listener-node-${{ matrix.node }}
path: listener/reports/node-compatibility/
if-no-files-found: warn
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ data/
test-data/
*.db
*.db-journal

# Isolated mutation sources and generated CI reports
.mutation-*/
reports/mutation/
reports/accessibility/
reports/node-compatibility/
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Before you start, make sure you have:

- **Rust** (stable) + WebAssembly target: `rustup target add wasm32-unknown-unknown`
- **Stellar CLI**: `cargo install --locked stellar-cli --features opt`
- **Node.js 22** (used by both listener and dashboard in CI)
- **Node.js 22** (default for listener and dashboard); the listener also supports Node.js 24. See [listener Node.js compatibility](listener/docs/node-compatibility.md) for the CI matrix and runtime assumptions.
- **Git**

For a detailed walkthrough including platform-specific notes, see [`CONTRIBUTOR_SETUP.md`](CONTRIBUTOR_SETUP.md).
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ NotifyChain is an open-source event monitoring and notification system designed

The project enables developers to build reactive decentralized applications without continuously polling the blockchain.

CI quality checks: [dashboard accessibility](docs/accessibility-automation.md),
[listener Node.js support](listener/docs/node-compatibility.md),
[critical event mutation testing](listener/docs/mutation-testing.md), and
[contract build reproducibility](docs/CONTRACT_BUILD_REPRODUCIBILITY.md).

## Table of Contents

1. [Architecture Overview](#architecture-overview)
Expand Down
4 changes: 4 additions & 0 deletions contract/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[toolchain]
channel = "1.90.0"
profile = "minimal"
targets = ["wasm32v1-none"]
1 change: 1 addition & 0 deletions dashboard/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules
dist
storybook-static
.DS_Store
accessibility-results/
9 changes: 9 additions & 0 deletions dashboard/jest.accessibility.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const base = require('./jest.config.cjs');

module.exports = {
...base,
testMatch: [
'<rootDir>/src/__tests__/accessibility.test.tsx',
'<rootDir>/src/__tests__/accessibility-flows.test.tsx',
],
};
4 changes: 1 addition & 3 deletions dashboard/jest.setup.cjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
require('@testing-library/jest-dom');

const { jest } = require('@types/jest');

const originalFetch = global.fetch;
const originalSetTimeout = global.setTimeout;
const originalSetInterval = global.setInterval;
Expand Down Expand Up @@ -136,7 +134,7 @@ function failureInjectionFetch(url, init) {
return originalFetch(url, init);
}

fobject.defineProperty(HTMLElement.prototype, 'clientHeight', {
Object.defineProperty(HTMLElement.prototype, 'clientHeight', {
configurable: true,
get() {
return 600;
Expand Down
1 change: 1 addition & 0 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"lint": "node ./node_modules/eslint/bin/eslint.js \"src/**/*.{ts,tsx}\" --max-warnings=0",
"format:check": "node ./node_modules/prettier/bin/prettier.cjs --check \"src/**/*.{ts,tsx}\" --config ../.prettierrc",
"test": "node ./node_modules/jest/bin/jest.js",
"test:a11y": "node ./node_modules/jest/bin/jest.js --config jest.accessibility.config.cjs --runInBand",
"test:wallet": "node ./node_modules/jest/bin/jest.js src/__tests__/wallet-integration.test.tsx",
"benchmark": "node ./node_modules/jest/bin/jest.js src/benchmark",
"storybook": "node ./node_modules/storybook/bin/index.cjs dev -p 6006",
Expand Down
Loading