This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit f688c682214bb7e49c7aee599ed36cf3580c815e Author: Stephen Mallette <[email protected]> AuthorDate: Fri Oct 9 15:56:15 2026 -0400 Run the tinker-review functional server on 3.x PRs The functional server now follows the PR's release line: HTTP with the V4 serializers on master, WebSocket with GraphSON V3 and GraphBinary V1 on 3.7-dev and 3.8-dev. Its URL says which. Assisted-by: Claude Code:claude-opus-5-5 --- .../tinker-review/references/functional-testing.md | 13 ++- .skills/tinker-review/scripts/functional/setup.js | 100 ++++++++++++++++----- 2 files changed, 87 insertions(+), 26 deletions(-) diff --git a/.skills/tinker-review/references/functional-testing.md b/.skills/tinker-review/references/functional-testing.md index a69d915f6b..148cf43648 100644 --- a/.skills/tinker-review/references/functional-testing.md +++ b/.skills/tinker-review/references/functional-testing.md @@ -38,8 +38,9 @@ share state. | `start --workDir <dir> [--port <n>]` | Reads `pr`/`repoPath` from `session.json`; adds a `build/` worktree on `pr-review/<pr>`; runs `mvn clean install -DskipTests` over the full reactor; locates the `*-standalone` assembly; writes a TinkerGraph config, an init script (binding `g` and `a`), and a server yaml; launches `bin/gremlin-server.sh` on a free port; polls until ready. Prints the handle as JSON and persists it to `functional.json`. | | `stop --workDir <dir>` | Reads `functional.json`, kills the server JVM, and removes the `build/` worktree. Also invoked automatically by `review.js` teardown. | -The handle: `{ port, url, pid, buildWorktree, assemblyDir, logFile }`. `url` is -the HTTP endpoint (`http://localhost:<port>/gremlin`) to hand the subagent. +The handle: `{ port, protocol, url, pid, buildWorktree, assemblyDir, logFile }`. `url` is +the endpoint to hand the subagent: `http://localhost:<port>/gremlin` for a master +(4.x) PR, `ws://localhost:<port>/gremlin` for a 3.7-dev or 3.8-dev PR. The build is the slow step (a full reactor build, minutes). If readiness times out, the error names `functional-server.log` in the work dir — inspect it for the @@ -52,8 +53,12 @@ Mirrors the Phase-1 server so a reviewer's queries look the same against either: - `g` — a standard traversal source over an empty TinkerGraph - `a` — the same graph `withComputer()`, for OLAP steps (`connectedComponent()`, …) -Serializers are the project defaults (GraphSON V4 + GraphBinary V4) over the -`HttpChannelizer`, so any current GLV client connects normally. +The server follows the shipped default of the PR's release line, which `start` +reads from the built source (`protocol` in the handle): on master the +`HttpChannelizer` with GraphSON V4 and GraphBinary V4; on 3.7-dev and 3.8-dev the +`WebSocketChannelizer` with GraphSON V3 and GraphBinary V1 and the session and +traversal op processors. A GLV client from the same line connects normally, so +brief the subagent with the drivers of that line. ## Driving it from the subagent diff --git a/.skills/tinker-review/scripts/functional/setup.js b/.skills/tinker-review/scripts/functional/setup.js index 7f3232afa8..10b1383dca 100644 --- a/.skills/tinker-review/scripts/functional/setup.js +++ b/.skills/tinker-review/scripts/functional/setup.js @@ -58,32 +58,90 @@ globals << [g : traversal().withEmbedded(graph)] globals << [a : traversal().withEmbedded(graph).withComputer()] `; +// The release lines ship different servers: master (4.x) answers HTTP with the +// V4 serializers, while 3.7-dev and 3.8-dev run the WebSocket server with +// GraphSON V3 / GraphBinary V1 and op processors. The PR's own source says which +// line it is on: only 4.x has GraphSONMessageSerializerV4. +const V4_SERIALIZER = "gremlin-util/src/main/java/org/apache/tinkerpop/gremlin/util/ser/GraphSONMessageSerializerV4.java"; + /** - * Build a self-contained server yaml for the functional test. Based on the - * project's shipped default (HttpChannelizer + GraphSON V4 / GraphBinary V4) - * with host/port pinned and the g/a bindings wired via an init script. Written - * fresh rather than parsed from the assembly so there is no YAML dependency and - * no textual patching of the shipped file. + * Which server the built source runs: `http` (4.x) or `websocket` (3.x). + * @param {string} buildWorktree + * @returns {"http"|"websocket"} */ -function serverYaml(port) { - return `host: localhost +export function serverProtocol(buildWorktree) { + return existsSync(join(buildWorktree, V4_SERIALIZER)) ? "http" : "websocket"; +} + +const SCRIPT_ENGINES = `scriptEngines: { + gremlin-lang: {}, + gremlin-groovy: { + plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, + org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, + org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/review-init.groovy]}}}}`; + +/** + * Build a self-contained server yaml for the functional test, following the + * shipped default of the PR's release line (see serverProtocol) with host/port + * pinned and the g/a bindings wired via an init script. Written fresh rather + * than parsed from the assembly so there is no YAML dependency and no textual + * patching of the shipped file. Only settings a review needs are set: metrics + * are left out because their setting names differ between lines. + * + * @param {number} port + * @param {"http"|"websocket"} protocol + */ +export function serverYaml(port, protocol) { + if (protocol === "http") { + return `host: localhost port: ${port} timeoutMillis: 30000 channelizer: org.apache.tinkerpop.gremlin.server.channel.HttpChannelizer graphs: { graph: conf/tinkergraph-review.properties} -scriptEngines: { - gremlin-lang: {}, - gremlin-groovy: { - plugins: { org.apache.tinkerpop.gremlin.server.jsr223.GremlinServerGremlinPlugin: {}, - org.apache.tinkerpop.gremlin.jsr223.ImportGremlinPlugin: {classImports: [java.lang.Math], methodImports: [java.lang.Math#*]}, - org.apache.tinkerpop.gremlin.jsr223.ScriptFileGremlinPlugin: {files: [scripts/review-init.groovy]}}}} +${SCRIPT_ENGINES} serializers: - { className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV4, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }} - { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4 } - { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4, config: { serializeResultToString: true }} strictTransactionManagement: false `; + } + return `host: localhost +port: ${port} +evaluationTimeout: 30000 +channelizer: org.apache.tinkerpop.gremlin.server.channel.WebSocketChannelizer +graphs: { + graph: conf/tinkergraph-review.properties} +${SCRIPT_ENGINES} +serializers: + - { className: org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3, config: { ioRegistries: [org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerIoRegistryV3] }} + - { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1 } + - { className: org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1, config: { serializeResultToString: true }} +processors: + - { className: org.apache.tinkerpop.gremlin.server.op.session.SessionOpProcessor, config: { sessionTimeout: 28800000 }} + - { className: org.apache.tinkerpop.gremlin.server.op.traversal.TraversalOpProcessor} +strictTransactionManagement: false +`; +} + +/** + * Write the review's graph config, init script and server yaml into a built + * server assembly. Returns the yaml path to launch `bin/gremlin-server.sh` with. + * + * @param {string} assemblyDir - a `*-standalone` server directory + * @param {number} port + * @param {"http"|"websocket"} protocol - from serverProtocol + * @returns {Promise<string>} + */ +export async function writeServerConfig(assemblyDir, port, protocol) { + await mkdir(join(assemblyDir, "conf"), { recursive: true }); + await mkdir(join(assemblyDir, "scripts"), { recursive: true }); + await writeFile(join(assemblyDir, "conf", "tinkergraph-review.properties"), GRAPH_PROPERTIES); + await writeFile(join(assemblyDir, "scripts", "review-init.groovy"), INIT_GROOVY); + const yamlPath = join(assemblyDir, "conf", "gremlin-server-review.yaml"); + await writeFile(yamlPath, serverYaml(port, protocol)); + return yamlPath; } /** @@ -112,7 +170,9 @@ async function findAssembly(buildWorktree) { /** * @typedef {object} FunctionalHandle * @property {number} port - localhost port the server listens on - * @property {string} url - base HTTP endpoint (`http://localhost:<port>/gremlin`) + * @property {"http"|"websocket"} protocol - which server the PR's line runs (see serverProtocol) + * @property {string} url - endpoint to hand the subagent: `http://localhost:<port>/gremlin` + * on 4.x, `ws://localhost:<port>/gremlin` on 3.x * @property {number} pid - PID of the server JVM (for teardown) * @property {string} buildWorktree - the `<workDir>/build` worktree to remove on teardown * @property {string} assemblyDir - the built `*-standalone` directory @@ -183,13 +243,8 @@ async function buildAndLaunch(workDir, buildWorktree, opts) { const assemblyDir = await findAssembly(buildWorktree); const port = opts.port || await findAvailablePort(); - // Write config into the assembly's conf/scripts dirs. - await mkdir(join(assemblyDir, "conf"), { recursive: true }); - await mkdir(join(assemblyDir, "scripts"), { recursive: true }); - await writeFile(join(assemblyDir, "conf", "tinkergraph-review.properties"), GRAPH_PROPERTIES); - await writeFile(join(assemblyDir, "scripts", "review-init.groovy"), INIT_GROOVY); - const yamlPath = join(assemblyDir, "conf", "gremlin-server-review.yaml"); - await writeFile(yamlPath, serverYaml(port)); + const protocol = serverProtocol(buildWorktree); + const yamlPath = await writeServerConfig(assemblyDir, port, protocol); // Launch natively in the foreground ("console" mode reads the yaml arg via the // catch-all case) and detach, capturing output to a log for diagnosis. @@ -204,7 +259,8 @@ async function buildAndLaunch(workDir, buildWorktree, opts) { ); child.unref(); - return { port, url: `http://localhost:${port}/gremlin`, pid: child.pid, buildWorktree, assemblyDir, logFile }; + const scheme = protocol === "http" ? "http" : "ws"; + return { port, protocol, url: `${scheme}://localhost:${port}/gremlin`, pid: child.pid, buildWorktree, assemblyDir, logFile }; } /**
