proto Documentation

repository·master·Indexed 23 days ago

https://github.com/moonrepo/proto

A high-performance, multi-language version manager written in Rust. proto provides a unified CLI for managing toolchains across languages like Node, Python, Go, and Rust using contextual version detection and a pluggable WASM-based architecture. It includes crates for system environment detection (system_env), version specification management (version_spec), and a WASM plugin framework via warpgate.

Tokens
40.5K
Snippets
44
Records
269
Agent score
78%

What's inside proto

  1. Overview of proto

    master

    proto is a pluggable, next-generation version manager designed to provide a unified toolchain for multiple programming languages. It is written in Rust for high performance and is used to power the moon toolchain.

    Key features include:

    • Multi-language support: A single CLI to manage versions for various languages.
    • Contextual version detection: Automatically ensures the correct tool version is used based on your current context.
    • Security: Includes checksum verification to ensure tools are from trusted sources.
    • Extensibility: Uses a pluggable architecture via WASM, allowing for custom integrations and plugins.
    • Ecosystem awareness: Detects and infers from a language's existing ecosystem for better compatibility.
  2. Overview of version_spec

    master

    The version_spec crate provides enums and utilities for managing version specifications. It is designed to handle the lifecycle of a version candidate, transitioning from an unresolved state (such as a requirement, range, alias, or partial version) to a fully resolved state (a specific version or alias).

    It supports two primary versioning schemes:

    • Semantic Versioning (semver)
    • Calendar Versioning (calver)
  3. Overview of warpgate_pdk

    master
    The warpgate_pdk crate provides reusable WebAssembly (WASM) macros and functions designed specifically for building plugin developer kits (PDKs). It serves as a foundational library to help developers create standardized and reusable logic for plugins within the proto ecosystem.
  4. Use warpgate_api for Warpgate plugin development

    master
    The warpgate_api crate provides the necessary APIs for interacting with Warpgate plugins. It is specifically designed to be used from the WebAssembly (WASM) layer, allowing developers to build extensible plugin logic that runs within the Warpgate environment.
  5. Supported languages in proto

    master

    proto supports a wide range of languages and package managers out of the box, including:

    • Bun
    • Deno
    • Go
    • moon
    • Node (including npm, pnpm, and yarn)
    • Python (including poetry and uv)
    • Ruby
    • Rust

    Additional tools can be supported via the pluggable WASM architecture.

  6. Use PluginContainer to manage Extism plugins

    master

    The PluginContainer is a wrapper around Extism's Plugin and Manifest types. It provides high-level methods for calling WASM functions using serde-compatible input and output types. It also includes built-in caching for function results to reduce the overhead of host-to-guest communication.

    Workflow:

    1. Create a Manifest: Use PluginManifest::new with a Wasm::file path.
    2. Instantiate Container: Use PluginContainer::new (with host functions) or PluginContainer::new_without_functions.
    3. Call Functions:
      • call_func / call_func_with: Standard calls using serde types.
      • cache_func / cache_func_with: Calls that cache the result for subsequent use.
      • call: For non-serde based functions.
    use warpgate::{Id, PluginContainer, PluginManifest, Wasm};
    
    // Load the plugin and create a manifest
    let wasm_file = loader.load_plugin(locator);
    let manifest = PluginManifest::new([Wasm::file(wasm_file)]);
    
    // Create a container
    let container = PluginContainer::new(Id::new("id")?, manifest, [host, funcs])?;
    // Or
    let container = PluginContainer::new_without_functions(Id::new("id")?, manifest)?;
    
    // Call and cache a function with serde types
    let output: AddOutput = container.cache_func_with("add", AddInput {
    	left: 10,
    	right: 20,
    })?;
    
    dbg!(output.sum);
  7. Understand proto E2E test ordering and phases

    master

    Tests are run alphabetically based on their filename. The numeric prefix in the filename (e.g., 01-name.sh) determines the execution phase. Because state accumulates across tests (earlier installs are visible to later tests), do not reorder tests without considering their dependencies.

    Phases by numeric range:

    • 00–09: Smoke (help, version listing)
    • 10–19: Standalone tool installs
    • 20–29: Dependent tool installs (e.g., npmnode)
    • 30–39: Backends (e.g., asdf)
    • 40–49: Cross-cutting checks (status, bin, run, shim, prototools, exec, outdated, pin/alias)
    • 50–59: Activation checks (activate, deactivate)
    • 90–99: Teardown (uninstall, clean)
  8. Debug WASM plugins

    master

    Debugging WASM plugins requires building a local debug target and pointing proto to that specific file via a .prototools configuration.

    1. Build the debug target

    Build the plugin for the wasm32-wasip1 target: cargo build --target wasm32-wasip1

    The resulting file will be located at target/wasm32-wasip1/debug/<name>.wasm.

    2. Configure .prototools

    Update your .prototools file to use the local .wasm file instead of a remote one:

    [plugins.tools]
    node-test = "file://./target/wasm32-wasip1/debug/node_plugin.wasm"

    3. Execute and Log

    WASM plugins cannot use println! or dbg!. You must use logging macros: error!, warn!, info!, debug!, or log!.

    Run the plugin with trace logging enabled to generate a <id>-debug.log file: PROTO_LOG=trace PROTO_WASM_LOG=trace PROTO_CACHE=off ~/proto/target/debug/proto run node-test

    [plugins.tools]
    node-test = "file://./target/wasm32-wasip1/debug/node_plugin.wasm"
    PROTO_LOG=trace PROTO_WASM_LOG=trace PROTO_CACHE=off ~/proto/target/debug/proto run node-test
  9. Initialize a PluginLoader

    master

    To manage and cache WASM plugins, instantiate a PluginLoader. You must provide two directory paths: a root directory for caching .wasm files and a temporary directory for downloading and unpacking files during the loading process.

    use warpgate::PluginLoader;
    
    let root = get_cache_root();
    let loader = PluginLoader::new(root.join("plugins"), root.join("temp"));
  10. Debug failing proto tests

    master

    Since proto is executed as a child process during tests (using create_proto_command), standard println! or dbg! statements in the proto codebase won't appear in your test output.

    To debug a failing test:

    1. Capture the assert() result into a variable.
    2. Comment out the .success() call (which panics on failure).
    3. Print the captured stdout and stderr using String::from_utf8_lossy.
    4. Use assert!(false) to force the test to continue so you can see the output.

    Note on WASM logs in tests: When running tests, WASM log files are written to the sandbox/fixture rather than the current directory. You must read them from the sandbox path: println!("{}", std::fs::read_to_string(sandbox.path().join("<id>-debug.log")).unwrap());

    #[test]
    fn installs_without_minor() {
        let sandbox = create_empty_sandbox();
    
        let mut cmd = create_proto_command(sandbox.path());
    
        // assign to variable
        let assert = cmd
            .arg("install")
            .arg("node")
            .arg("17")
            .arg("--")
            .arg("--no-bundled-npm")
            .assert();
        // .success();
    
        // print captured output
        println!("{}", String::from_utf8_lossy(&assert.get_output().stdout));
        println!("{}", String::from_utf8_lossy(&assert.get_output().stderr));
    
        assert!(sandbox.path().join("tools/node/17.9.1").exists());
    
        // force test to fail
        assert!(false);
    }
  11. Publish a plugin to the proto registry

    master

    To make a plugin available to the community via proto plugin search, you must add an entry to the registry/data/third-party.json file in the repository.

    1. Open registry/data/third-party.json.
    2. Add a new object to the plugins array containing your tool's information. At a minimum, you must provide an id.
    3. For a full list of available and required fields, refer to the TypeScript interface used by the registry.
    4. Validate and format the dataset using one of the commands below.
    5. Commit your changes and create a pull request.
    {
      "plugins": [
        // ...
        {
          "id": "my-new-tool"
          // ...
        }
      ]
    }