moonlit-pdk 0.2.0

SDK for writing Moonlit release-pipeline plugins as WebAssembly components.
Documentation

moonlit-pdk

SDK for writing Moonlit release-pipeline plugins as WebAssembly (wasm32-wasip2) components.

A Moonlit plugin is a cdylib that implements one or more middlewares over a Context (HTTP, process exec, filesystem, environment, logging, and clock capabilities, each gated by the host's deny-by-default sandbox). The moonlit_plugin! macro (from moonlit-pdk-macros, re-exported here) wires your middlewares and optional plugin config/state into the generated component entrypoints.

use moonlit_pdk::prelude::*;

/// Input for the `greet` middleware, bound from the step's config block.
/// `JsonSchema` is what lets `moonlit plugin inspect` and the registry
/// document these fields.
#[derive(Deserialize, Default, schemars::JsonSchema)]
#[serde(rename_all = "camelCase", default)]
struct GreetInput {
    /// Who to greet; defaults to "world" when empty.
    name: String,
}

/// Output published for downstream steps to read as
/// `steps.<step>.outputs.greeting`.
#[derive(Serialize, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
struct GreetOutput {
    /// The greeting message, e.g. `hello, world`.
    greeting: String,
}

#[derive(Default)]
struct Greet;

impl Middleware for Greet {
    const NAME: &'static str = "greet";
    const DESCRIPTION: &'static str = "logs and returns a greeting";
    type Input = GreetInput;
    type Output = GreetOutput;

    fn execute(&self, ctx: &Context, input: Self::Input) -> MiddlewareResult<Self::Output> {
        let who = if input.name.is_empty() {
            "world".to_string()
        } else {
            input.name
        };
        ctx.log_info(&format!("greeting {who}"));
        MiddlewareResult::ok(GreetOutput {
            greeting: format!("hello, {who}"),
        })
    }
}

moonlit_plugin! { name: "greet-plugin", middlewares: [Greet] }

Each middleware declares two types. Input is deserialized from the step's config block (a #[serde(default)] struct is the idiomatic "all fields optional" choice); Output is serialized and published for later steps to read as steps.<step>.outputs.<field>. Use NoInput or NoOutput when a middleware takes no configuration or publishes nothing. Both types derive schemars::JsonSchema, and the macro emits those schemas into the component so moonlit plugin inspect and the registry can document the plugin without running it.

Alongside moonlit-pdk, a plugin crate needs serde (with derive), serde_json, and schemars = "1". moonlit plugin new scaffolds all of this. The example above is what it generates.

Build with cargo build --target wasm32-wasip2 --release (requires rustup target add wasm32-wasip2).

License

Licensed under either of MIT or Apache-2.0 at your option.