diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..cbe1d0b --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,12 @@ +## Summary + +- + +## Validation + +- [ ] `pnpm guardian:check` +- [ ] `pnpm test` + +## Notes + +Confirm that process state, webhook idempotency, reconciliation and settlement outbox behavior remain compatible. diff --git a/.github/workflows/guardian.yml b/.github/workflows/guardian.yml new file mode 100644 index 0000000..5fccd04 --- /dev/null +++ b/.github/workflows/guardian.yml @@ -0,0 +1,39 @@ +name: MAVULA Guardian + +on: + pull_request: {} + push: + branches: + - main + workflow_dispatch: {} + +permissions: + contents: read + +concurrency: + group: settlements-guardian-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + guardian: + name: guardian + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Setup pnpm + uses: pnpm/action-setup@v6 + with: + version: 10.33.0 + - name: Use Node.js 22.22.3 + uses: actions/setup-node@v6 + with: + node-version: 22.22.3 + cache: pnpm + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Run guardian + run: pnpm guardian:check + - name: Build + run: pnpm build + - name: Test + run: pnpm test diff --git a/package.json b/package.json index 6a91acc..6e02cdb 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "description": "MAVULA Settlements - payment adapter and reconciliation foundation.", - "author": "EstandarMustaq ", + "author": "EstandarMustaq ", "license": "AGPL-3.0-only", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -19,6 +19,7 @@ }, "scripts": { "build": "tsc -p tsconfig.json", + "guardian:check": "node scripts/guardian.mjs", "test": "tsc -p tsconfig.json && node --test test/*.test.mjs", "test:ci": "tsc -p tsconfig.json && node --test test/*.test.mjs", "typecheck": "tsc -p tsconfig.json --noEmit", diff --git a/scripts/guardian.mjs b/scripts/guardian.mjs new file mode 100644 index 0000000..fd591b1 --- /dev/null +++ b/scripts/guardian.mjs @@ -0,0 +1,70 @@ +#!/usr/bin/env node + +import { existsSync, readFileSync } from "node:fs"; +import { spawnSync } from "node:child_process"; + +const failures = []; + +function fail(message) { + failures.push(message); +} + +function read(path) { + return readFileSync(path, "utf8"); +} + +function json(path) { + return JSON.parse(read(path)); +} + +function requireFile(path) { + if (!existsSync(path)) fail(`${path} is required`); +} + +const pkg = json("package.json"); + +if (pkg.name !== "@mavula/settlements") fail("package name must be @mavula/settlements"); +if (pkg.license !== "AGPL-3.0-only") fail("settlements must remain AGPL-3.0-only"); +if (pkg.author !== "EstandarMustaq ") { + fail("author must use the MAVULA address"); +} +if (!pkg.types) fail("settlements must publish TypeScript declarations"); + +[ + ".github/CODEOWNERS", + ".github/PULL_REQUEST_TEMPLATE.md", + ".github/workflows/guardian.yml", + "LICENSE", + "README.md", + "prisma/schema.prisma", + "tsconfig.json", +].forEach(requireFile); + +if (!/SPDX-License-Identifier: AGPL-3\.0-only/.test(read("LICENSE"))) { + fail("LICENSE must declare AGPL SPDX"); +} +if (!/@mavula\/settlements/.test(read("README.md"))) { + fail("README must identify @mavula/settlements"); +} + +const tracked = spawnSync("git", ["ls-files"], { encoding: "utf8" }); +if (tracked.status !== 0) fail("git ls-files failed"); +for (const file of tracked.stdout.split("\n").filter(Boolean)) { + if (/(^|\/)\.env($|\.(?!example$))/.test(file)) fail(`${file} must not be tracked`); +} + +for (const path of ["package.json", "README.md", ".github/CODEOWNERS"]) { + if (path === "scripts/guardian.mjs") continue; + const content = read(path); + if (/getfluxo-io|@getfluxo|packages\/fengine|packages\/fwk|packages\/fpay|packages\/finfra/.test(content)) { + fail(`${path} contains legacy public identifiers`); + } +} + +if (failures.length > 0) { + console.error("MAVULA settlements guardian failed:"); + for (const failure of failures) console.error(`- ${failure}`); + process.exit(1); +} + +console.log("MAVULA settlements guardian passed.");