From 1c502606f195156eae80348fb874faf0f2ffd767 Mon Sep 17 00:00:00 2001 From: Estandar <143508382+EstandarMustaq@users.noreply.github.com> Date: Thu, 9 Jul 2026 10:53:16 +0200 Subject: [PATCH] Add workbench guardian --- .github/CODEOWNERS | 1 + .github/PULL_REQUEST_TEMPLATE.md | 12 ++++++ .github/workflows/guardian.yml | 64 +++++++++++++++++++++++++++++ package.json | 1 + scripts/guardian.mjs | 70 ++++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/guardian.yml create mode 100644 scripts/guardian.mjs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..b0c154d --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @EstandarMustaq diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..9a48cf6 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,12 @@ +## Summary + +- + +## Validation + +- [ ] `pnpm guardian:check` +- [ ] `pnpm test:all` + +## Notes + +Confirm that queue processing, retries, schedules, status and metrics remain compatible. diff --git a/.github/workflows/guardian.yml b/.github/workflows/guardian.yml new file mode 100644 index 0000000..708de1c --- /dev/null +++ b/.github/workflows/guardian.yml @@ -0,0 +1,64 @@ +name: MAVULA Guardian + +on: + pull_request: {} + push: + branches: + - main + workflow_dispatch: {} + +permissions: + contents: read + +concurrency: + group: workbench-guardian-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + guardian: + name: guardian + runs-on: ubuntu-latest + services: + redis: + image: redis:7-alpine + ports: + - 6379:6379 + options: >- + --health-cmd "redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=5 + env: + REDIS_URL: redis://127.0.0.1:6379 + INTERNAL_API_KEY: guardian-internal-api-key + LEDGER_CORE_URL: http://fengine.test + 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 + - name: Prepare settlements workspace + run: | + branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}" + if git ls-remote --exit-code --heads https://github.com/mavulahq/settlements "$branch"; then + git clone --depth 1 --branch "$branch" https://github.com/mavulahq/settlements ../settlements + else + git clone --depth 1 https://github.com/mavulahq/settlements ../settlements + fi + cat > pnpm-workspace.yaml <<'YAML' + packages: + - . + - ../settlements + YAML + - name: Install dependencies + run: pnpm install --no-frozen-lockfile + - name: Run guardian + run: pnpm guardian:check + - name: Build settlements package + run: pnpm --filter @mavula/settlements build + - name: Build + run: pnpm build + - name: Test + run: pnpm test:all diff --git a/package.json b/package.json index a9e6977..d70211d 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "main": "dist/main.js", "scripts": { "build": "tsc -p tsconfig.json", + "guardian:check": "node scripts/guardian.mjs", "start": "node dist/main.js", "start:dev": "ts-node-dev --respawn --transpile-only src/main.ts", "test": "jest --runInBand", diff --git a/scripts/guardian.mjs b/scripts/guardian.mjs new file mode 100644 index 0000000..c82c371 --- /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/workbench") fail("package name must be @mavula/workbench"); +if (pkg.license !== "AGPL-3.0-only") fail("workbench must remain AGPL-3.0-only"); +if (pkg.dependencies?.["@mavula/settlements"] !== "workspace:*") { + fail("workbench must depend on @mavula/settlements through the workspace"); +} + +[ + ".github/CODEOWNERS", + ".github/PULL_REQUEST_TEMPLATE.md", + ".github/workflows/guardian.yml", + "LICENSE", + "README.md", + "jest.config.js", + "jest.e2e.config.js", + "tsconfig.json", +].forEach(requireFile); + +if (!/SPDX-License-Identifier: AGPL-3\.0-only/.test(read("LICENSE"))) { + fail("LICENSE must declare AGPL SPDX"); +} +if (!/@mavula\/workbench/.test(read("README.md"))) { + fail("README must identify @mavula/workbench"); +} + +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 workbench guardian failed:"); + for (const failure of failures) console.error(`- ${failure}`); + process.exit(1); +} + +console.log("MAVULA workbench guardian passed.");