Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/ci-quality-gates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,14 +281,14 @@ jobs:
# draft-changelog first applies the full master.xml chain to the empty DB via
# liquibase:update — so this step is ALSO the empty→head migration-chain replay gate (a
# changelog that doesn't apply fails here). Keep that apply intact.
scripts/db-utils.sh draft-changelog || ISSUES_FOUND+=("Schema check / migration-chain apply failed. Run: bun run db:draft-changelog")
bun run db:draft-changelog || ISSUES_FOUND+=("Schema check / migration-chain apply failed. Run: bun run db:draft-changelog")
if [ -f "server/application/src/main/resources/db/changelog_new.xml" ]; then
echo "::error::Schema drift detected. Run: bun run db:draft-changelog"
cat server/application/src/main/resources/db/changelog_new.xml
ISSUES_FOUND+=("Schema drift detected")
fi

scripts/db-utils.sh generate-erd || ISSUES_FOUND+=("ERD generation failed. Run: bun run db:generate-erd-docs")
bun run db:generate-erd-docs || ISSUES_FOUND+=("ERD generation failed. Run: bun run db:generate-erd-docs")
git add docs/contributor/erd/schema.mmd
if ! git diff --cached --quiet docs/contributor/erd/schema.mmd; then
echo "::error::ERD outdated. Run: bun run db:generate-erd-docs"
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ jobs:
- 'bunfig.toml'
application-server:
- 'server/**'
# check:env, check:contracts, check:diagrams and db-utils.sh all live here and run on
# the App Server or Database leg.
# Repository tooling runs on the App Server or Database leg.
- 'scripts/**'
# test:agents and typecheck:agents cover the precompute tree.
- 'docker/agents/**'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/qualify-bun-lockfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
- "docs/package.json"
- "webapp/package.json"
- "scripts/check-package-manager.ts"
- "scripts/qualify-bun-lockfile.sh"
- "scripts/qualify-bun-lockfile.ts"
- ".github/actions/setup-bun/action.yml"
- ".github/workflows/qualify-bun-lockfile.yml"
- "docker/agents/pi/Dockerfile"
Expand All @@ -31,4 +31,4 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: ./.github/actions/setup-bun
- run: scripts/qualify-bun-lockfile.sh 25
- run: bun run qualify:bun-lockfile -- 25
5 changes: 3 additions & 2 deletions docs/contributor/e2e-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ read -rsp "LLM key: " E2E_LLM_KEY && echo && export E2E_LLM_KEY
export E2E_LLM_PRICING_MODE=PRICED
export E2E_LLM_INPUT_USD="$YOUR_CONTRACT_INPUT_RATE_PER_1M"
export E2E_LLM_OUTPUT_USD="$YOUR_CONTRACT_OUTPUT_RATE_PER_1M"
scripts/e2e-setup.sh \
bun run e2e:setup -- \
--account-login group/subgroup \
--repo group/subgroup/project \
--llm-base-url https://llm.example/v1 \
Expand All @@ -56,7 +56,8 @@ Existing resources are reused only when their immutable SCM and model-routing fi

## Running the review

The script prints the dev-trigger command after sync has produced a suitable artifact:
The setup output identifies the workspace and, when one has synced, the selected artifact. Use those
IDs with the dev trigger:

```bash
JWT="$(curl -fsS -i -X POST http://localhost:8080/auth/dev-login \
Expand Down
2 changes: 1 addition & 1 deletion docs/contributor/local-development.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ the stored configuration. New connections and models start inactive. Test the co
its price (or explicitly mark it as having no metered API cost), then activate it and bind it to a
purpose (`PRACTICE_REVIEW` or `MENTOR`) on the workspace's **AI models** page.

For an automated local end-to-end setup, use `scripts/e2e-setup.sh`. It creates the connection,
For an automated local end-to-end setup, use `bun run e2e:setup`. It creates the connection,
model, and agent binding through the application API and keeps provider credentials out of source
control and command-line history by reading them from `E2E_*` environment variables.

Expand Down
4 changes: 2 additions & 2 deletions jean.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"scripts": {
"setup": "bash \"$JEAN_ROOT_PATH/scripts/jean-setup.sh\"",
"run": "bash ./scripts/jean-public-test.sh start"
"setup": "bun \"$JEAN_ROOT_PATH/scripts/jean-setup.ts\"",
"run": "bun ./scripts/jean-public-test.ts start"
}
}
14 changes: 9 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"format:config:check": "oxfmt --check *.ts *.json .oxfmtrc.json .oxlintrc.json .changeset/*.cjs .changeset/*.json .vscode/*.json scripts/tsconfig.json project.code-workspace",
"lint:agents": "oxlint server docker scripts docs .changeset .github.qkg1.topmitlint.config.ts",
"lint:agents:fix": "oxlint --fix server docker scripts docs .changeset .github.qkg1.topmitlint.config.ts",
"check:agents": "bun run format:agents:check && bun run format:docs:check && bun run format:config:check && bun run lint:agents && bun run typecheck:agents && bun run typecheck:scripts && bun test scripts/run-security-mutations.test.ts",
"check:agents": "bun run format:agents:check && bun run format:docs:check && bun run format:config:check && bun run lint:agents && bun run typecheck:agents && bun run typecheck:scripts && bun run test:tooling",
"check:agents:fix": "bun run format:agents && bun run format:docs && bun run format:config && oxlint --fix server docker scripts docs .changeset .github.qkg1.topmitlint.config.ts",
"ci:agents": "bun run format:agents:check && bun run format:docs:check && bun run format:config:check && oxlint -f github server docker scripts docs .changeset .github.qkg1.topmitlint.config.ts",
"format:server": "bun run format:java",
Expand All @@ -60,9 +60,9 @@
"lint": "bun --bun run-s lint:server lint:client lint:agents",
"check": "bun --bun run-s check:package-manager check:agent-runtime-pins check:java-nullness check:server check:client check:agents test:agents check:stories check:story-sort check:components check:diagrams check:env check:contracts check:instructions check:changesets docs:lint",
"typecheck": "bun run typecheck:webapp && bun run typecheck:scripts && bun run typecheck:agents",
"db:draft-changelog": "scripts/db-utils.sh draft-changelog",
"db:generate-erd-docs": "scripts/db-utils.sh generate-erd",
"check:ports": "scripts/check-ports.sh",
"db:draft-changelog": "bun scripts/db-utils.ts draft-changelog",
"db:generate-erd-docs": "bun scripts/db-utils.ts generate-erd",
"check:ports": "bun scripts/check-ports.ts",
"format:achievements": "bun scripts/format-achievements.ts",
"github:update-schema": "bun scripts/update-github-schema.ts",
"gitlab:update-schema": "bun scripts/update-gitlab-schema.ts",
Expand All @@ -89,7 +89,11 @@
"check:instructions": "bun scripts/check-agent-instructions.ts && bun test scripts/check-agent-instructions.test.ts",
"check:changesets": "bun test scripts/verify-changesets.test.ts scripts/sync-release-version.test.ts",
"prepare:server:generated": "cd server && ./mvnw -pl generated-clients -am install -DskipTests --batch-mode",
"check:package-manager": "bun scripts/check-package-manager.ts"
"check:package-manager": "bun scripts/check-package-manager.ts",
"e2e:setup": "bun scripts/e2e-setup.ts",
"jean:public-test": "bun scripts/jean-public-test.ts",
"qualify:bun-lockfile": "bun scripts/qualify-bun-lockfile.ts",
"test:tooling": "bun test scripts/*.test.ts"
},
"devDependencies": {
"@changesets/cli": "2.31.1",
Expand Down
43 changes: 29 additions & 14 deletions scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,34 @@

Utility scripts for Hephaestus development. Bun executes TypeScript scripts directly.

## Prerequisites

Scripts use dependencies from the root `package.json`. Run `bun install` at the repo root first.
Most scripts require `bun install` at the repository root. Jean setup performs that installation; the
lockfile qualifier intentionally starts from a clean dependency tree.

## Available Scripts

### Database Utilities
### Repository orchestration

Database documentation commands use `db-utils.sh` and require Docker with the Compose plugin:
Substantive developer orchestration under `scripts/` uses typed Bun entry points:

```bash
bun run db:generate-erd-docs # Generate Mermaid ERD diagram
bun run db:draft-changelog # Generate Liquibase changelog diff
```
| Command | Purpose |
| --- | --- |
| `bun run check:ports` | Validate configured local ports and report listeners. |
| `bun run db:draft-changelog` | Rebuild a disposable database and generate a Liquibase diff. |
| `bun run db:generate-erd-docs` | Apply migrations and regenerate the Mermaid ERD. |
| `bun run e2e:setup -- <options>` | Configure a local E2E workspace against the selected SCM and model provider. Secrets are environment-only. |
| `bun run jean:public-test -- <command>` | Manage the machine-local public test route. |

The database commands require Docker with the Compose plugin.

`jean:public-test` accepts `start`, `stop`, `status`, `smoke`, and `seed-status`. The route is
internet-facing and requires Docker, the machine's Coolify network and Traefik configuration
directory, and `server/.env`.

Jean runs `bun "$JEAN_ROOT_PATH/scripts/jean-setup.ts"` from a new worktree to copy machine-local
configuration and install dependencies. It is not a general contributor command.

`bun run qualify:bun-lockfile -- 25` is a maintainer and CI diagnostic. Each iteration removes the
root, webapp, and docs `node_modules` directories before reinstalling with the frozen lockfile.

**ERD generation environment variables:**

Expand Down Expand Up @@ -64,10 +78,11 @@ bun run nats:extract-examples -- --event push --event pull_request:opened
| `--until <iso>` | Only messages before this timestamp |
| `--dry-run` | Validate config without extracting |

## Dependencies
## Retained shell boundaries

The retained shell files run where shell is already part of the runtime:

Scripts depend on packages in root `package.json`:
- `.husky/_/husky.sh` is Husky's generated, minimal Git-hook bootstrap.
- `webapp/docker/entrypoint.sh` prepares assets in the final nginx image, which does not contain Bun.

- `commander` - CLI parsing
- `pg` and `@types/pg` - PostgreSQL client with TypeScript types
- `@nats-io/jetstream` and `@nats-io/transport-node` - NATS JetStream client
No substantive shell script is permitted under `scripts/`.
124 changes: 0 additions & 124 deletions scripts/check-ports.sh

This file was deleted.

88 changes: 88 additions & 0 deletions scripts/check-ports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { connect } from "node:net";
import { join } from "node:path";

import { positivePort, readEnvFile } from "./lib/env.ts";

export interface ServicePort {
name: string;
port: number;
}

export function duplicatePorts(services: ServicePort[]): Array<[ServicePort, ServicePort]> {
const seen = new Map<number, ServicePort>();
const duplicates: Array<[ServicePort, ServicePort]> = [];
for (const service of services) {
const previous = seen.get(service.port);
if (previous) duplicates.push([service, previous]);
else seen.set(service.port, service);
}
return duplicates;
}

export function isListening(port: number, host = "127.0.0.1"): Promise<boolean> {
return new Promise((resolve) => {
const socket = connect({ host, port });
socket.setTimeout(500);
socket.once("connect", () => socket.destroy());
socket.once("close", (hadError) => resolve(!hadError));
socket.once("error", () => resolve(false));
socket.once("timeout", () => socket.destroy());
});
}

async function isPortListening(port: number): Promise<boolean> {
return (await Promise.all([isListening(port, "127.0.0.1"), isListening(port, "::1")])).some(
Boolean,
);
}

async function main(): Promise<void> {
const args = Bun.argv.slice(2);
if (args.some((arg) => !["--quiet", "-q"].includes(arg))) {
console.error("Usage: bun scripts/check-ports.ts [--quiet]");
process.exitCode = 2;
return;
}
const quiet = args.length > 0;
const fileEnv = await readEnvFile(join(import.meta.dirname, "../server/.env"));
const value = (name: string, fallback: string): string =>
Bun.env[name] ?? fileEnv[name] ?? fallback;
let services: ServicePort[];
try {
services = [
{ name: "PostgreSQL", port: positivePort(value("POSTGRES_PORT", "5432"), "POSTGRES_PORT") },
{
name: "Application server",
port: positivePort(value("SERVER_PORT", "8080"), "SERVER_PORT"),
},
{ name: "Webapp (Vite)", port: positivePort(value("WEBAPP_PORT", "4200"), "WEBAPP_PORT") },
];
} catch (error) {
console.error(error instanceof Error ? error.message : String(error));
process.exitCode = 2;
return;
}
const duplicates = duplicatePorts(services);
if (!quiet) {
console.log("\nHephaestus port availability check\n===================================\n");
for (const [service, previous] of duplicates)
console.log(` DUPLICATE :${service.port} ${service.name} conflicts with ${previous.name}`);
if (duplicates.length) console.log("\nWarning: Duplicate port assignments detected.\n");
}
const results = await Promise.all(
services.map(async (service) => ({ service, busy: await isPortListening(service.port) })),
);
const occupied = results.filter(({ busy }) => busy);
if (!quiet) {
for (const { service, busy } of results)
console.log(` ${busy ? "OCCUPIED" : "FREE"} :${service.port} ${service.name}`);
console.log(
occupied.length
? `\n${occupied.length} port(s) already in use.\n`
: "\nAll ports are available.\n",
);
}
if (occupied.length || duplicates.length) process.exitCode = 1;
}

if (import.meta.main) await main();
Loading
Loading