Skip to content

feat(cli): add plugin invocation protocol#2

Closed
QMalcolm wants to merge 3 commits into
qmalcolm--feat-plugin-discoveryfrom
qmalcolm--feat-plugin-io-protocol
Closed

feat(cli): add plugin invocation protocol#2
QMalcolm wants to merge 3 commits into
qmalcolm--feat-plugin-discoveryfrom
qmalcolm--feat-plugin-io-protocol

Conversation

@QMalcolm

@QMalcolm QMalcolm commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Stacked on #1

Summary

  • Adds Request, Response, and Issue JSON envelope types for the plugin stdin/stdout protocol
  • Adds Invoke(ctx, pluginDir, invoke, req, pluginStderr) — runs a plugin subprocess, pipes the request JSON to stdin, captures stdout as the response, and enforces a context timeout
  • A non-empty Issues slice in the response is not a Go error; the caller (defined in a later segment of work) will decide whether error-severity issues produce a non-zero exit code

Migrated from apache/ossie#155 following the project's move to the Apache Software Foundation. This PR targets a branch in my fork because it stacks on #1 — will retarget to apache/ossie:main once the base of the stack merges.

QMalcolm added 3 commits July 7, 2026 14:19
C1 (osi convert base) needs a subprocess layer to pipe requests to
plugins and receive responses. This adds the Request/Response/Issue
envelope types and an Invoke() function that owns the full lifecycle:
marshal request to stdin, capture stdout, forward stderr, enforce
context timeout, and decode the JSON response.

Design decisions:
- Invoke takes pluginDir and invoke []string directly rather than
  *Plugin — decouples the invocation layer from the discovery types
  and makes the function independently testable
- cmd.Stdout assigned as *bytes.Buffer rather than using cmd.Output()
  — cmd.Output() is incompatible with a pre-assigned cmd.Stderr writer
  and would silently discard the caller's stderr destination
- ctx.Err() checked after cmd.Run() for timeout detection — exec
  kills the process with SIGKILL and returns "signal: killed", not
  context.DeadlineExceeded, so the context must be checked explicitly
- Issue.Path uses omitempty — an absent key is semantically distinct
  from an empty string; plugins that have no path omit the field
- A non-empty Issues slice is NOT a Go error — the caller (C1) is
  responsible for inspecting severities and determining exit code

Tests use the TestMain subprocess self-invocation pattern: the test
binary re-invokes itself with GO_TEST_PLUGIN=1, enters the fake
plugin branch in TestMain, and exits without running any tests.
This avoids compiling a separate helper binary and works portably.

Caveats:
- File collection, output writing, issue rendering, and wiring into
  cmd/convert.go are deferred to C1
A Request with nil Files marshals as {"files":null} rather than
{"files":{}}. Plugins that iterate over the files object may crash
on a null value. Guard added at the top of Invoke(); test added to
verify the wire format is always an object.
An empty invoke slice would panic with an index out of range on
invoke[0]. While F2 validation prevents this in normal usage, an
explicit check with a descriptive error is safer and makes the
failure mode obvious if the invariant is ever violated.
@QMalcolm

QMalcolm commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

Superseded by apache/ossie#7 — converting the stack to draft PRs opened directly against apache/ossie:main.

@QMalcolm QMalcolm closed this Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant