Файли
openidea/plugins/build.sh
T
l0ng-ai 78e0c7a23e feat(plugins): finalize plugin API before release, concurrent providers
Plugin API (breaking; the plugin system is unreleased):
- Remove the runtime `contributions` interface (register-command,
  register-language, register-theme). Commands, languages and themes are
  declared in plugin.toml only, so everything a plugin contributes goes
  through the install preview and consent. The registry drops runtime
  scope, `contribute` and `dispose_runtime`; inline `definition` in
  language and theme declarations is gone.
- `completion-item.kind` is a string (the 25 LSP kinds; unknown kinds show
  no icon), like the other open-ended kinds.
- `lifecycle.activate` and `commands.run` return `types.error` instead of a
  string. The host maps it to the matching error kind, and only `failed`
  counts against the plugin: cancelling, a missing grant or an unknown
  command does not. The SDK's `Plugin` returns `Result<_, Error>`; plain
  messages convert to `Error::Failed`.

Runtime:
- Editor providers of several plugins are called at the same time
  (`Actor::submit` + `Pending::wait`): the wait is the slowest plugin's,
  not the sum, so each gets the full edit-path budget.

Versioning and tooling:
- Components are checked at install and by `openidea-plugin check` for the
  plugin API they were built for; another major or a newer minor is
  refused with what to do.
- Tests: the SDK's WIT matches the host's, a simulated next-minor host
  links every shipped component, every host function has an SDK wrapper,
  released APIs only grow (no snapshot until the first release).
- plugins/build.sh builds every checked-in component, records a source
  fingerprint next to it, and `--check` fails on stale components; CI runs
  it plus fmt, clippy (host and wasm) and tests for the SDK, the official
  plugins and the fixtures.
- The API evolution rules and the checklist for a new host function are in
  plugins/README.md; the design notes under docs/ are removed.
2026-11-04 02:41:46 +08:00

96 рядки
3.7 KiB
Bash
Виконуваний файл

#!/usr/bin/env bash
# Builds the repository's Rust plugins into WebAssembly components
# (wasm32-wasip2 emits a component directly; no cargo-component / wasm-tools).
#
# plugins/build.sh # every component the repo checks in
# plugins/build.sh hello # one plugin (a name under plugins/, or a path)
# plugins/build.sh --check [...] # change nothing; fail if a checked-in component
# # is stale or a plugin doesn't build (CI)
#
# Output: <plugin>/dist/plugin.wasm (referenced by `wasm = ...` in plugin.toml)
# and <plugin>/dist/plugin.wasm.sources, a fingerprint of the sources it was
# built from: the plugin (minus target/ and dist/), the SDK and wit/. Builds
# are not byte-reproducible across machines, so --check compares fingerprints,
# not bytes. CARGO_TARGET_DIR, when set, is shared by all plugins.
set -euo pipefail
here="$(cd "$(dirname "$0")" && pwd)"
repo="$(cd "$here/.." && pwd)"
rustup target list --installed | grep -qx wasm32-wasip2 || rustup target add wasm32-wasip2
check=0
if [ "${1:-}" = "--check" ]; then
check=1
shift
fi
sha256() { if command -v sha256sum >/dev/null; then sha256sum; else shasum -a 256; fi; }
# Lists "<path> <sha256>" for the files git sees (tracked, or new and not
# ignored) under the given paths of directory $1, minus build output and Finder
# metadata; so a
# checkout fingerprints the same as the working tree that committed it.
list() {
local dir="$1"
shift
(cd "$dir" && git ls-files --cached --others --exclude-standard -- "$@") |
{ grep -Ev '(^|/)(dist|target)/|(^|/)\.DS_Store$' || true; } | LC_ALL=C sort -u | while read -r f; do
if [ -f "$dir/$f" ]; then printf '%s %s\n' "$f" "$(sha256 <"$dir/$f" | cut -d' ' -f1)"; fi
done
}
# Prints the fingerprint of everything plugin $1 is built from.
fingerprint() {
{ list "$1" .; list "$repo" sdk/rust/openidea-plugin/Cargo.toml sdk/rust/openidea-plugin/src sdk/rust/openidea-plugin/wit wit; } |
sha256 | cut -d' ' -f1
}
failed=0
build() {
local dir="$1"
local name target out
name="$(sed -n 's/^name *= *"\(.*\)"/\1/p' "$dir/Cargo.toml" | head -1)"
target="${CARGO_TARGET_DIR:-$dir/target}"
out="$target/wasm32-wasip2/release/${name//-/_}.wasm"
local rel="${dir#"$repo"/}"
if [ "$check" = 1 ]; then
if ! CARGO_TARGET_DIR="$target" cargo build -q --locked --manifest-path "$dir/Cargo.toml" --release --target wasm32-wasip2; then
echo "FAIL $rel: does not build"
failed=1
return
fi
local want have
want="$(fingerprint "$dir")"
have="$(cat "$dir/dist/plugin.wasm.sources" 2>/dev/null || true)"
if [ "$want" != "$have" ]; then
echo "STALE $rel: dist/plugin.wasm was not built from the current sources; run plugins/build.sh $rel"
failed=1
else
echo "ok $rel"
fi
return
fi
echo "building $name ($rel)"
CARGO_TARGET_DIR="$target" cargo build --manifest-path "$dir/Cargo.toml" --release --target wasm32-wasip2
mkdir -p "$dir/dist"
cp "$out" "$dir/dist/plugin.wasm"
fingerprint "$dir" >"$dir/dist/plugin.wasm.sources"
echo " -> $rel/dist/plugin.wasm ($(wc -c <"$dir/dist/plugin.wasm" | tr -d " ") bytes)"
}
if [ $# -gt 0 ]; then
for p in "$@"; do
if [ -d "$here/$p" ]; then build "$here/$p"
elif [ -d "$repo/$p" ]; then build "$(cd "$repo/$p" && pwd)"
elif [ -d "$p" ]; then build "$(cd "$p" && pwd)"
else echo "no plugin at $p" >&2; exit 2
fi
done
else
# Every checked-in component built from a plugin.toml + Cargo.toml directory.
for manifest in "$here"/*/Cargo.toml "$here"/official/*/Cargo.toml "$repo"/crates/ide-plugin/tests/fixtures/*/Cargo.toml; do
dir="$(dirname "$manifest")"
[ -f "$dir/plugin.toml" ] && build "$dir"
done
fi
exit "$failed"