diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 28c9ed4a..36f9fc75 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -9,6 +9,9 @@ jobs:
- macos-latest
- ubuntu-latest
- windows-latest
+ buf-bin-setup:
+ - buf-on-path
+ - buf-not-on-path
runs-on: ${{ matrix.os }}
steps:
- name: checkout
@@ -19,6 +22,7 @@ jobs:
node-version-file: ".nvmrc"
cache: npm
- uses: bufbuild/buf-action@v1
+ if: matrix.buf-bin-setup == 'buf-on-path'
with:
setup_only: true
- name: install-deps
@@ -32,24 +36,32 @@ jobs:
- name: format
run: npm run format
- name: integration-tests-with-xvfb
+ env:
+ BUF_INSTALLED: ${{ matrix.buf-bin-setup }}
run: xvfb-run -a npm run test:integration
if: runner.os == 'Linux'
- name: integration-tests
shell: bash
+ env:
+ BUF_INSTALLED: ${{ matrix.buf-bin-setup }}
run: npm run test:integration
if: runner.os != 'Linux'
- name: playwright-tests
run: npm run test:playwright
# Limiting playwright tests to macOS for now due to issues with xvfb on Linux and
# timeouts on windows
- if: runner.os == 'macOS'
+ #
+ # NOTE: We disable playwright tests when buf is not installed on the system $PATH
+ # for now so we don't have the extension attempting to resolve the installation.
+ # We'll need to find a way to intercept the call from playwright's VS Code extension.
+ if: runner.os == 'macOS' && matrix.buf-bin-setup == 'buf-on-path'
- name: check diff
run: node scripts/gh-diffcheck.mjs
- name: upload-playwright-test-results
uses: actions/upload-artifact@v4
if: always()
with:
- name: test-results
+ name: test-results-${{ matrix.buf-bin-setup }}
path: test-results/
retention-days: 5
if-no-files-found: ignore
diff --git a/README.md b/README.md
index 7bf3854b..4502ecb2 100644
--- a/README.md
+++ b/README.md
@@ -1,122 +1,89 @@
# Buf for Visual Studio Code
The [VS Code Buf extension][vs-code-marketplace] helps you work with [Protocol Buffers][protobuf]
-files in a much more intuitive way, adding smart syntax highlighting, navigation, formatting,
+files in a much more intuitive way, adding semantic syntax highlighting, navigation, formatting,
documentation and diagnostic hovers, and integrations with [Buf][buf] commands.
## Features
-- **Code navigation** - Go-to definition and documentation insets for `.proto` symbols.
-- **Syntax highlighting** - Protobuf specific color and styling of code.
-- **Code editing** - Formatting via `buf format` and annotations and hovers based on `buf lint`.
-- **Documentation hovers** - Documentation for definitions when hovering a reference.
-- **Buf command support** - Execution of `buf` CLI commands via the [Command Palette][command-palette].
+- Code navigation: Go-to definition and go-to references for `.proto` symbols.
+- Autocompletion: Completion results for `.proto` symbols using [IntelliSense][intellisense].
+- Syntax highlighting: Protobuf specific color and styling for code.
+- Documentation hovers: Documentation for definitions when hovering a reference.
+- Formatting: Formats `.proto` files on-save.
+- Diagnostics: Annotations and highlights for build and lint errors.

-## Getting Started
-
-[Install the latest version via the VS Code marketplace][vs-code-marketplace].
-
-By default, the extension will use your locally-installed version of `buf` on your system
-`$PATH`. However, you don't have to install `buf` - the extension can manage and install it
-for you based on the [buf.commandLine.path](#buf.commandline.path) and [buf.commandLine.version](#buf.commandline.version)
-configurations:
-
-| |
buf.commandLine.path
| buf.commandLine.version
|
-| --- | --- | --- |
-| Default: Use `buf` from the system `$PATH`. | {empty} | {empty} |
-| Use the latest released version of `buf` and check for updates on extension activation. | {empty} | `latest` |
-| Use `buf` at specified path. | User specified path | {empty} |
-| Install and use the specified version of `buf`. | {empty} | User specified semver version |
-| Use `buf` at specified path and display an error message. | User specified path | User specified semver version |
-
-## Extension Settings
-
-This extension contributes the following configuration settings.
-
-### buf.commandLine.path
+In addition to integrated editing features, the extension provides commands through the
+`buf` CLI. These commands are accessible by opening the [Command Palette][command-palette],
+`Ctrl/Cmd+Shift+P`. See the [full list of commands](#commands) provided by this extension.
-Default: `null`
-The path to a specific install of Buf to use. Relative paths are supported and are relative to the VS Code workspace root.
+## Requirements
-### buf.commandLine.version
+- Visual Studio Code 1.95 or newer (or editors compatible with VS Code 1.90+ APIs)
-Default: `null`
-Specific version (e.g. 'v1.53.0') of Buf release to download and install.
-
-### buf.restartAfterCrash
-
-Default: `true`
-Automatically restart the Buf Language Server (up to 4 times) if it crashes.
-
-### buf.enableHover
-
-Default: `true`
-Enable hover features provided by the language server.
-
-### buf.enable
+## Getting Started
-Default: `true`
-Enable Buf Language Server features.
+[Install the latest version via the VS Code marketplace][vs-code-marketplace].
-### buf.debug
+You do not need to install the Buf CLI to use this extension. By default, the extension uses
+the Buf CLI from your system `$PATH`. If `buf` isn't found on your `$PATH`, the extension
+automatically downloads and installs the latest version to its own storage directory.
-Default: `false`
-Enable debug logs in output channels.
+## Community and Support
-### buf.log-format
+Feedback is welcome and appreciated! For feature requests, bugs, or questions, please
+[file an issue][issue].
-Default: `text`
-Buf Language Server log format.
+If you're looking for help and/or discussion around Protobuf, best practices, etc., join us
+on [Slack][slack].
## Commands
-This extension contributes the following commands to the [Command Palette][command-palette].
-
-### Setup
-
-- Install CLI: installs the `buf` CLI based on `buf.commandLine.path` and `buf.commandLine.version`
- configurations and then attempts to start the language server.
-- Update CLI: updates the `buf` CLI based on `buf.commandLine.path` and `buf.commandLine.version`
- configurations and then attempts to start the language server.
-
-### Language Server
+A full list of [Command Palette][command-palette] commands provided by this extension:
- Start Buf Language Server: starts the Buf Language Server. If the Buf Language Server is
already running, it will stop and then start it.
+
- Stop Buf Language Server: stops the Buf Language Server. If the Buf Language Server is not
currently running, then it is a no-op.
-### Buf
+- Build: runs `buf build` with optional user input for the build output file. If the build
+ output is specified by the user, it will be created at the root of each VS Code workspace.
-- Build: runs `buf build` with an optional user input for the build output.
- Init: runs `buf config init` at the root of each VS Code workspace. This creates a `buf.yaml` file
to help users get started with Buf modules and workspaces.
-- List available breaking change detection rules: lists the breaking change detection rules
- that are available.
-- List available lint rules: lists the lint rules that are available.
-- Prune module dependencies: prunes unused dependencies from the `buf.lock` at the root of
- each VS Code workspace.
-- Update module dependencies: updates the dependencies in `buf.lock` at the root of each
- VS Code workspace.
-- Generate: runs `buf generate` at the root of each VS Code workspace.
-- List module files: lists the Protobuf definition files for the Buf module/workspace at the
- root of each VS Code workspace.
-- Price of BSR paid plans: provides the pricing information for Buf Schema Registry (BSR)
- for each VS Code workspace.
-- Module stats: provides Buf module/workspace stats at the root of each VS Code workspace.
-
-### Extension
-
-- Show Buf Output: shows the extension output channel
+
+- List available breaking change detection rules: runs `buf config ls-breaking-rules` at the
+ root of each VS Code workspace and provides a list of available [breaking change detection rules][breaking-rules]
+ in a VS Code editor window.
+
+- List available lint rules: runs `buf config ls-lint-rules` at the root of each VS Code workspace
+ and provides a list of available [lint rules][lint-rules] in a VS Code editor window.
+
+- Prune module dependencies: runs `buf dep prune` at the root of each VS Code workspace and
+ prunes unused dependencies from the `buf.lock` file(s).
+
+- Update module dependencies: runs `buf dep update` at the root of each VS Code workspace and
+ updates the dependencies in the `buf.lock` file(s).
+
+- Generate: runs `buf generate` at the root of each VS Code workspace and generates code based
+ on the `buf.gen.yaml` file(s).
+
+- Show Buf Output: shows the extension output channel.
## Legal
Offered under the [Apache 2 license][license].
-[command-palette]: https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette
-[vs-code-marketplace]: https://marketplace.visualstudio.com/items?itemName=bufbuild.vscode-buf
-[protobuf]: https://protobuf.dev/
[buf]: https://buf.build/
+[breaking-rules]: https://buf.build/docs/breaking/rules
+[command-palette]: https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette
+[issue]: https://github.com/bufbuild/vscode-buf/issues/new/choose
+[intellisense]: https://code.visualstudio.com/docs/editing/intellisense
[license]: https://github.com/bufbuild/vscode-buf/blob/main/LICENSE
+[lint-rules]: https://buf.build/docs/lint/rules
+[protobuf]: https://protobuf.dev/
+[slack]: https://buf.build/links/slack
+[vs-code-marketplace]: https://marketplace.visualstudio.com/items?itemName=bufbuild.vscode-buf
diff --git a/package.json b/package.json
index 3e5f87a8..e579187d 100644
--- a/package.json
+++ b/package.json
@@ -75,12 +75,6 @@
"icon": "$(list-unordered)",
"title": "List available lint rules."
},
- {
- "command": "buf.configlsmodules",
- "category": "Buf",
- "icon": "$(folder-library)",
- "title": "List modules in workspace."
- },
{
"command": "buf.depprune",
"category": "Buf",
@@ -102,34 +96,6 @@
"title": "Generate",
"description": "Run `buf generate` across VS Code workspace(s)."
},
- {
- "command": "buf.lsfiles",
- "category": "Buf",
- "icon": "$(files)",
- "title": "List module files.",
- "description": "List module files across VS Code workspace(s)."
- },
- {
- "command": "buf.price",
- "category": "Buf",
- "icon": "$(briefcase)",
- "title": "Price for BSR paid plans.",
- "description": "Check the price of BSR paid plans across VS Code workspace(s)."
- },
- {
- "command": "buf.stats",
- "category": "Buf",
- "icon": "$(graph-line)",
- "title": "Module stats",
- "description": "Get stats for Buf modules across VS Code workspace(s)."
- },
- {
- "command": "buf.install",
- "category": "Buf",
- "icon": "$(cloud-download)",
- "title": "Install CLI",
- "description": "Install the Buf CLI from GitHub releases."
- },
{
"command": "buf.showOutput",
"category": "Buf",
@@ -147,74 +113,15 @@
"category": "Buf",
"icon": "$(debug-stop)",
"title": "Stop Buf Language Server"
- },
- {
- "command": "buf.update",
- "category": "Buf",
- "icon": "$(sync)",
- "title": "Update CLI",
- "description": "Check for updates and install the latest version of the Buf CLI."
}
],
"configuration": {
"title": "Buf",
"properties": {
- "buf.commandLine.path": {
- "type": "string",
- "description": "The path to a specific install of Buf to use. Relative paths are supported and are relative to the workspace root."
- },
- "buf.commandLine.version": {
- "type": "string",
- "description": "Specific version (git tag e.g. 'v1.53.0') of Buf release to download and install."
- },
- "buf.restartAfterCrash": {
- "type": "boolean",
- "default": true,
- "description": "Automatically restart Buf (up to 4 times) if it crashes."
- },
- "buf.enableHover": {
- "type": "boolean",
- "default": true,
- "description": "Enable hover features provided by the language server."
- },
- "buf.enable": {
- "type": "boolean",
- "default": true,
- "description": "Enable Buf language server features."
- },
- "buf.debug": {
+ "buf.debugLogs": {
"type": "boolean",
"default": false,
- "description": "Enable debug mode."
- },
- "buf.log-format": {
- "type": [
- "string",
- "null"
- ],
- "enum": [
- "text",
- "color",
- "json"
- ],
- "default": "text",
- "description": "Buf language server log format."
- },
- "buf.checks.breaking.againstStrategy": {
- "type": "string",
- "enum": [
- "disk",
- "git"
- ],
- "default": "git",
- "description": "The strategy to use when checking breaking changes against a specific reference.",
- "deprecationMessage": "Deprecated: breaking change detection is no longer supported in the LSP."
- },
- "buf.checks.breaking.againstGitRef": {
- "type": "string",
- "default": "refs/remotes/origin/HEAD",
- "description": "The Git reference to check breaking changes against.",
- "deprecationMessage": "Deprecated: breaking change detection is no longer supported in the LSP."
+ "description": "Enable debug logs for the Buf language server."
}
}
},
diff --git a/playwright.config.ts b/playwright.config.ts
index c286e57c..c0e505c5 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -13,8 +13,9 @@ export default defineConfig({
reporter: process.env.CI ? "html" : "list",
timeout: 120_000_000,
expect: {
- timeout: 40_000,
+ timeout: 60_000,
},
+ globalSetup: "./test/playwright/global-setup",
projects: [
{
name: "VS Code insiders",
diff --git a/src/commands/buf-config-ls-modules.ts b/src/commands/buf-config-ls-modules.ts
deleted file mode 100644
index 34e6cc51..00000000
--- a/src/commands/buf-config-ls-modules.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import * as vscode from "vscode";
-import { log } from "../log";
-import { bufState } from "../state";
-import { Command } from "./command";
-
-/**
- * Minimum Buf version required for `buf config ls-modules` command.
- */
-const minBufVersion = "v1.34.0";
-
-/**
- * bufConfigLsModules shows the output channel and runs `buf config ls-modules` at the root
- * of each VS Code workspace folder. If there are no workspace folders, then bufConfigLsModules
- * displays a warning and is a no-op.
- */
-export const bufConfigLsModules = new Command(
- "buf.configlsmodules",
- "COMMAND_TYPE_BUF",
- async () => {
- log.show();
- if (!vscode.workspace.workspaceFolders) {
- log.warn(`No workspace found, unable to run "buf config ls-modules"`);
- return;
- }
- const bufVersion = bufState.getBufBinaryVersion();
- if (bufVersion?.compare(minBufVersion) === -1) {
- log.warn(
- `Current Buf Version ${bufVersion} does not meet minimum required version ${minBufVersion}, unable to run "buf config ls-modules".`
- );
- return;
- }
- for (const workspaceFolder of vscode.workspace.workspaceFolders) {
- bufState.execBufCommand(
- ["config", "ls-modules"],
- workspaceFolder.uri.path
- );
- }
- }
-);
diff --git a/src/commands/buf-ls-files.ts b/src/commands/buf-ls-files.ts
deleted file mode 100644
index 8283f4eb..00000000
--- a/src/commands/buf-ls-files.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as vscode from "vscode";
-import { log } from "../log";
-import { bufState } from "../state";
-import { Command } from "./command";
-
-/**
- * bufLsFiles shows the output channel and runs `buf ls-files` at the root of each VS Code
- * workspace folder. If there are no workspace folders, then bufLsFiles displays a warning
- * and is a no-op.
- */
-export const bufLsFiles = new Command(
- "buf.lsfiles",
- "COMMAND_TYPE_BUF",
- async () => {
- log.show();
- if (!vscode.workspace.workspaceFolders) {
- log.warn(`No workspace found, unable to run "buf ls-files"`);
- return;
- }
- for (const workspaceFolder of vscode.workspace.workspaceFolders) {
- bufState.execBufCommand(["ls-files"], workspaceFolder.uri.path);
- }
- }
-);
diff --git a/src/commands/buf-price.ts b/src/commands/buf-price.ts
deleted file mode 100644
index 73128fa2..00000000
--- a/src/commands/buf-price.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import * as vscode from "vscode";
-import { log } from "../log";
-import { bufState } from "../state";
-import { Command } from "./command";
-
-/**
- * Buf version with the most recent price update.
- */
-const lastPriceUpdate = "v1.28.0";
-
-/**
- * Minimum Buf version required for `buf beta price` command.
- */
-const minBetaVersion = "v1.16.0";
-
-/**
- * bufPrice shows the output channel and runs `buf beta price` at the root of each VS Code
- * workspace folder. If there are no workspace folders, then bufPrice displays a warning
- * and is a no-op.
- */
-export const bufPrice = new Command(
- "buf.price",
- "COMMAND_TYPE_BUF",
- async () => {
- log.show();
- if (!vscode.workspace.workspaceFolders) {
- log.warn(`No workspace found, unable to run "buf beta price"`);
- return;
- }
- const bufVersion = bufState.getBufBinaryVersion();
- if (bufVersion?.compare(minBetaVersion) === -1) {
- log.warn(
- `Current Buf Version ${bufVersion} does not meet minimum required version of price command ${minBetaVersion}, unable to run "buf beta price".`
- );
- return;
- }
- if (bufVersion?.compare(lastPriceUpdate) === -1) {
- log.warn(
- `Current Buf Version ${bufVersion} has outdated price data, latest price update available for version ${lastPriceUpdate} and onwards.`
- );
- }
- for (const workspaceFolder of vscode.workspace.workspaceFolders) {
- bufState.execBufCommand(["beta", "price"], workspaceFolder.uri.path);
- }
- }
-);
diff --git a/src/commands/buf-stats.ts b/src/commands/buf-stats.ts
deleted file mode 100644
index e48a0cf0..00000000
--- a/src/commands/buf-stats.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as vscode from "vscode";
-import { log } from "../log";
-import { bufState } from "../state";
-import { Command } from "./command";
-
-/**
- * Minimum Buf version required for `buf stats` command.
- */
-const minBufVersion = "v1.55.0";
-
-/**
- * Minimum Buf version required for `buf beta stats` command.
- */
-const minBetaVersion = "v1.17.0";
-
-/**
- * bufStats shows the output channel and runs `buf stats` at the root of each VS Code
- * workspace folder. If there are no workspace folders, then bufStats displays a warning
- * and is a no-op.
- */
-export const bufStats = new Command(
- "buf.stats",
- "COMMAND_TYPE_BUF",
- async () => {
- log.show();
- if (!vscode.workspace.workspaceFolders) {
- log.warn(`No workspace found, unable to run "buf stats"`);
- return;
- }
- const bufVersion = bufState.getBufBinaryVersion();
- let args = ["stats"];
- if (bufVersion?.compare(minBufVersion) === -1) {
- args = ["beta", "stats"];
- if (bufVersion?.compare(minBetaVersion) === -1) {
- log.warn(
- `Current Buf Version ${bufVersion} does not meet minimum required version of stats command ${minBetaVersion}, unable to run "buf stats".`
- );
- return;
- }
- }
- for (const workspaceFolder of vscode.workspace.workspaceFolders) {
- bufState.execBufCommand(args, workspaceFolder.uri.path);
- }
- }
-);
diff --git a/src/commands/install-buf.ts b/src/commands/install-buf.ts
deleted file mode 100644
index 9f65c374..00000000
--- a/src/commands/install-buf.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { bufState } from "../state";
-import { Command } from "./command";
-
-/**
- * installBuf installs the Buf CLI binary and attempts to start the language server after.
- */
-export const installBuf = new Command(
- "buf.install",
- "COMMAND_TYPE_SETUP",
- async (ctx) => {
- await bufState.installBufBinary(ctx.globalStorageUri.fsPath);
- await bufState.startLanguageServer(ctx);
- }
-);
diff --git a/src/commands/register-all-commands.ts b/src/commands/register-all-commands.ts
index 8e54acc6..70fa5383 100644
--- a/src/commands/register-all-commands.ts
+++ b/src/commands/register-all-commands.ts
@@ -4,19 +4,13 @@ import { bufBuild } from "./buf-build";
import { bufConfigInit } from "./buf-config-init";
import { bufConfigLsBreakingRules } from "./buf-config-ls-breaking-rules";
import { bufConfigLsLintRules } from "./buf-config-ls-lint-rules";
-import { bufConfigLsModules } from "./buf-config-ls-modules";
import { bufDepPrune } from "./buf-dep-prune";
import { bufDepUpdate } from "./buf-dep-update";
import { bufGenerate } from "./buf-generate";
-import { bufLsFiles } from "./buf-ls-files";
-import { bufPrice } from "./buf-price";
-import { bufStats } from "./buf-stats";
-import { installBuf } from "./install-buf";
import { showCommands } from "./show-commands";
import { showOutput } from "./show-output";
import { startLanguageServer } from "./start-lsp";
import { stopLanguageServer } from "./stop-lsp";
-import { updateBuf } from "./update-buf";
/**
* @file Provides a convenience function for registering all commands in the extension.
@@ -28,19 +22,13 @@ const commands = [
bufConfigInit,
bufConfigLsBreakingRules,
bufConfigLsLintRules,
- bufConfigLsModules,
bufDepPrune,
bufDepUpdate,
bufGenerate,
- bufLsFiles,
- bufPrice,
- bufStats,
- installBuf,
showCommands,
showOutput,
startLanguageServer,
stopLanguageServer,
- updateBuf,
];
/**
diff --git a/src/commands/update-buf.ts b/src/commands/update-buf.ts
deleted file mode 100644
index fdb7a373..00000000
--- a/src/commands/update-buf.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { bufState } from "../state";
-import { Command } from "./command";
-
-/**
- * updateBuf updates the Buf CLI binary and attempts to start the language server after.
- */
-export const updateBuf = new Command(
- "buf.update",
- "COMMAND_TYPE_SETUP",
- async (ctx) => {
- await bufState.updateBufBinary(ctx.globalStorageUri.fsPath);
- await bufState.startLanguageServer(ctx);
- }
-);
diff --git a/src/extension.ts b/src/extension.ts
index e735dd8a..2d4b2743 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -1,9 +1,9 @@
import * as vscode from "vscode";
-import { installBuf } from "./commands/install-buf";
import { registerAllCommands } from "./commands/register-all-commands";
import { startLanguageServer } from "./commands/start-lsp";
import { stopLanguageServer } from "./commands/stop-lsp";
import { log } from "./log";
+import { bufState } from "./state";
import { activateStatusBar, deactivateStatusBar } from "./status-bar";
/**
@@ -15,7 +15,7 @@ export async function activate(ctx: vscode.ExtensionContext) {
ctx.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(handleOnDidConfigChange)
);
- await installBuf.execute();
+ await bufState.init(ctx);
}
/**
@@ -35,13 +35,9 @@ const handleOnDidConfigChange = async (e: vscode.ConfigurationChangeEvent) => {
if (!e.affectsConfiguration("buf")) {
return;
}
- if (
- e.affectsConfiguration("buf.commandLine.path") ||
- e.affectsConfiguration("buf.commandLine.version")
- ) {
- await installBuf.execute();
- }
- if (e.affectsConfiguration("buf.enable")) {
+ if (e.affectsConfiguration("buf.debugLogs")) {
+ // When debug logs configuration changes, restart the language server.
+ await stopLanguageServer.execute();
await startLanguageServer.execute();
}
};
diff --git a/src/github.ts b/src/github.ts
index 7782327d..3f4f9aae 100644
--- a/src/github.ts
+++ b/src/github.ts
@@ -13,11 +13,8 @@ import { progress } from "./progress";
/**
* The GitHub release URL for the Buf CLI.
- *
- * Exported for tests.
*/
-export const githubReleaseURL =
- "https://api.github.com/repos/bufbuild/buf/releases/";
+const githubReleaseURL = "https://api.github.com/repos/bufbuild/buf/releases/";
/**
* Release is a GitHub release for the Buf CLI.
diff --git a/src/state.ts b/src/state.ts
index d328faa7..bf5ad7f4 100644
--- a/src/state.ts
+++ b/src/state.ts
@@ -48,6 +48,8 @@ const minBufVersion = "v1.59.0";
* - The `buf` CLI binary used by the extension
* - The LSP server (e.g. starting and stopping)
*
+ * @method init initializes the state of the Buf extension by setting up the buf CLI binary.
+ * This is expected to be called when the extension is activated.
* @method handleExtensionStatus sets the extension status on the state to the provided
* status, and once the work is complete, sets the extension status back to idle.
* @method getExtensionStatus gets the current extension status.
@@ -55,10 +57,6 @@ const minBufVersion = "v1.59.0";
* @method getBufBinaryVersion gets the current Buf binary version.
* @method getBufBinaryPath gets the current Buf binary path.
* @method execBufCommand execs the Buf binary with the specified arguments and working directory.
- * @method installBufBinary installs the Buf CLI for the extension based on the extension
- * configuration.
- * @method updateBufBinary updates the Buf CLI for the extension based on the extension
- * configuration.
* @method startLanguageServer starts the LSP server and client.
* @method stopLanguageServer stops the language server and client.
*
@@ -77,10 +75,6 @@ class BufState {
public constructor() {
effect(() => {
switch (this._languageServerStatus.value) {
- case "LANGUAGE_SERVER_NOT_INSTALLED":
- this.bufBinary = undefined;
- this.lspClient = undefined;
- break;
case "LANGUAGE_SERVER_DISABLED":
this.lspClient = undefined;
break;
@@ -183,156 +177,50 @@ class BufState {
}
/**
- * installBufBinary installs the Buf CLI for the extension based on the extension configuration.
- *
- * There are two configuration fields for managing the Buf CLI binary:
- * buf.commandLine.path: a path to a local binary, e.g. /usr/local/bin/buf
- * buf.commandLine.version: the expected version of the Buf binary, e.g. v1.53.0. This can
- * also be set to "latest", in which case, the extension will check for and expect the latest
- * released version of the Buf CLI.
+ * init initializes the state of the Buf extension by setting up the buf CLI binary and
+ * starting up the LSP.
*
- * When checking for the Buf CLI binary, the resolution logic uses the following order of
- * precendence:
- * 1. The path set on buf.commandLine.path.
- * 2. The version set to buf.commandLine.version. If "latest" is set, check for and use the
- * latest released version of the Buf CLI.
- * 3. If neither buf.commandLine.path or buf.commandLine.version is set, look for the Buf
- * CLI on the OS path.
+ * We check the user's system $PATH for buf. If the user has buf installed locally, then
+ * we use their locally installed version of buf.
*
- * If the Buf CLI for the configured path is already installed, then installBufBinary logs
- * this and is a no-op.
+ * If the user does not have the buf binary installed locally, then the extension will
+ * attempt to install the latest version of buf to the VS Code global storage and use that.
*/
- public async installBufBinary(storagePath: string) {
- let configPath = config.get("commandLine.path");
- const configVersion = config.get("commandLine.version");
-
- if (configPath) {
- if (!path.isAbsolute(configPath)) {
- try {
- configPath = getBinaryPathForRelConfigPath(configPath);
- } catch (e) {
- log.error(`Error loading buf from relative config path: ${e}`);
- this._languageServerStatus.value = "LANGUAGE_SERVER_NOT_INSTALLED";
- return;
- }
- }
- if (configVersion) {
- log.warn(
- "Both 'buf.commandLine.path' and 'buf.commandLine.version' are set. Using 'buf.commandLine.path'."
- );
- }
- if (this.bufBinary && this.bufBinary.path === configPath) {
- log.info(
- `Buf CLI for configured path '${configPath}' already installed`
- );
- return;
- }
- try {
- log.info(`Installing Buf CLI set to path '${configPath}...`);
- this.bufBinary = await getBufBinaryFromPath(configPath);
- log.info(
- `Using '${this.bufBinary.path}', version: ${this.bufBinary.version}.`
- );
- } catch (e) {
- log.error(`Error loading buf from path '${configPath}': ${e}`);
- this._languageServerStatus.value = "LANGUAGE_SERVER_NOT_INSTALLED";
- }
- return;
- }
- if (configVersion) {
- await this.updateBufBinary(storagePath);
- return;
- }
+ public async init(ctx: vscode.ExtensionContext) {
log.info("Looking for Buf on the system $PATH...");
try {
this.bufBinary = await findBufInSystemPath();
} catch (e) {
- log.error(`Buf is not installed on the OS path: ${e}`);
- this._languageServerStatus.value = "LANGUAGE_SERVER_NOT_INSTALLED";
- }
- }
-
- /**
- * updateBufBinary updates the Buf CLI for the extension based on the extension configuration.
- *
- * The version is specified by the buf.commandLine.version configuration.
- *
- * updateBufBinary will download and install the configured version of the Buf CLI if there
- * is currently no version of the Buf CLI used by the extension or if the current version
- * used does not match the configured version.
- *
- * If an explicit path to the Buf CLI binary is specified via buf.commandLine.path, then
- * updateBufBinary displays a warning and is a no-op.
- *
- * If no version is set, then updateBuf displays a warning and is a no-op.
- *
- * If the version set is not valid semver, then updateBufBinary displays a warning and is
- * a no-op.
- *
- * If the version set cannot be resolved, then updateBufBinary will provide the user with
- * a pop-up with the error message and a link to the installation docs.
- */
- public async updateBufBinary(storagePath: string) {
- if (config.get("commandLine.path")) {
- vscode.window.showErrorMessage(
- "'buf.commandLine.path' is explicitly set, no updates will be made."
+ log.info(
+ `Buf not found on the OS path: ${e}, installing from releases...`
);
- return;
- }
- const configVersion = config.get("commandLine.version");
- if (!configVersion) {
- vscode.window.showErrorMessage(
- "'buf.commandLine.version' is not set, no updates will be made."
- );
- return;
- }
- if (configVersion !== "latest") {
- if (!semver.valid(configVersion)) {
- log.error(
- `buf.commandLine.version '${configVersion}' is not a valid semver version, no updates installed for Buf CLI...`
- );
- return;
- }
- }
- if (
- this.bufBinary &&
- configVersion !== "latest" &&
- this.bufBinary.version.compare(configVersion) === 0
- ) {
- log.info(`Already installed Buf CLI version '${configVersion}',`);
- return;
- }
- const abort = new AbortController();
- try {
- log.info(`Checking github releases for '${configVersion}' release...`);
- const release = await github.getRelease(configVersion);
- const asset = await github.findAsset(release);
- this.bufBinary = await installReleaseAsset(
- storagePath,
- release,
- asset,
- abort
- );
- vscode.window.showInformationMessage(
- `Buf ${release.name} is now installed.`
- );
- } catch (e) {
- if (!abort.signal.aborted) {
- log.info(`Failed to install buf: ${e}`);
- this._languageServerStatus.value = "LANGUAGE_SERVER_NOT_INSTALLED";
- showPopup(
- `Failed to install Buf CLI. You may want to install it manually.`,
- "https://buf.build/docs/cli/installation/"
+ const abort = new AbortController();
+ try {
+ const release = await github.getRelease("latest");
+ const asset = await github.findAsset(release);
+ this.bufBinary = await installReleaseAsset(
+ ctx.globalStorageUri.fsPath,
+ release,
+ asset,
+ abort
);
+ } catch (e) {
+ if (!abort.signal.aborted) {
+ log.info(`Failed to install buf: ${e}`);
+ this._languageServerStatus.value = "LANGUAGE_SERVER_DISABLED";
+ showPopup(
+ `Failed to install Buf CLI. You may want to install it manually.`,
+ "https://buf.build/docs/cli/installation/"
+ );
+ }
}
}
+ this.startLanguageServer(ctx);
}
/**
* startLanguageServer starts the LSP server and client.
*
- * If the LSP is disabled through configuration, then startLanguageServer will display
- * a warning, set the appropriate status, and be a no-op.
* If the LSP server is already running (or already starting), then startLanguageServer
* will log a warning and be a no-op.
* If the LSP server is stopped or in an errored state, startLanguageServer will attempt
@@ -349,14 +237,11 @@ class BufState {
serverOutputChannel = createConsoleOutputChannel("Buf (server)");
ctx.subscriptions.push(serverOutputChannel);
}
- if (!config.get("enable")) {
- await this.stopLanguageServer();
- this._languageServerStatus.value = "LANGUAGE_SERVER_DISABLED";
- log.warn("Buf is disabled. Enable it by setting 'buf.enable' to true.");
- return;
- }
if (this.lspClient) {
- if (this._languageServerStatus.value === "LANGUAGE_SERVER_STARTING") {
+ if (
+ this._languageServerStatus.value === "LANGUAGE_SERVER_STARTING" ||
+ this._languageServerStatus.value === "LANGUAGE_SERVER_RUNNING"
+ ) {
log.warn("Buf Language Server already starting, no new actions taken.");
return;
}
@@ -368,18 +253,12 @@ class BufState {
this._languageServerStatus.value = "LANGUAGE_SERVER_STARTING";
return;
}
- if (this._languageServerStatus.value === "LANGUAGE_SERVER_RUNNING") {
- log.warn("Buf Language Server already running, restarting.");
- await this.stopLanguageServer();
- this._languageServerStatus.value = "LANGUAGE_SERVER_STARTING";
- return;
- }
}
if (!this.bufBinary) {
log.error(
"No installed version of Buf found, cannot start Buf Language Server."
);
- this._languageServerStatus.value = "LANGUAGE_SERVER_NOT_INSTALLED";
+ this._languageServerStatus.value = "LANGUAGE_SERVER_STOPPED";
return;
}
const args = getBufArgs();
@@ -398,13 +277,10 @@ class BufState {
documentSelector: protoDocumentSelector,
diagnosticCollectionName: "bufc",
outputChannel: serverOutputChannel,
- // TODO: we can consider making this configurable through our settings.
revealOutputChannelOn: lsp.RevealOutputChannelOn.Never,
middleware: {
+ // Always configure a hover provider on the client.
provideHover: async (document, position, token, next) => {
- if (!config.get("enableHover")) {
- return null;
- }
return next(document, position, token);
},
},
@@ -414,9 +290,8 @@ class BufState {
serverOptions,
clientOptions
);
- const errorHandler = this.lspClient.createDefaultErrorHandler(
- config.get("restartAfterCrash") ? 4 : 0
- );
+ // Always restart buf LSP if it crashes, up to 4 times.
+ const errorHandler = this.lspClient.createDefaultErrorHandler(4);
this.lspClient.clientOptions.errorHandler = {
error: (error, message, count) => {
return errorHandler.error(error, message, count);
@@ -456,23 +331,6 @@ class BufState {
*/
export const bufState = new BufState();
-/**
- * A helper function for getting the binary path based on a relative path config. We check
- * each workspace folder and return the first relative binary path that exists, otherwise
- * return undefined.
- */
-function getBinaryPathForRelConfigPath(configPath: string): string {
- if (vscode.workspace.workspaceFolders) {
- for (const workspaceFolder of vscode.workspace.workspaceFolders) {
- const joinedPath = path.join(workspaceFolder.uri.path, configPath);
- if (fs.existsSync(joinedPath)) {
- return joinedPath;
- }
- }
- }
- throw new Error(`Unable to use relative Buf binary path ${configPath}`);
-}
-
/**
* BufBinary contains the Buf CLI binary information used by the extension.
*
@@ -532,16 +390,18 @@ async function installReleaseAsset(
await fs.promises.access(downloadBin);
// We await for the bufBinary to be set before returning so we can catch any errors.
const bufBinary = await getBufBinaryFromPath(downloadBin);
+ log.info(`Using buf version v${bufBinary.version} from extension cache.`);
return bufBinary;
} catch (e) {
// In the case of an error, we log, and then move on to attempt a download.
- log.error(`Error accessing buf binary, downloading... ${e}`);
+ log.info(`No buf binary available locally, downloading... ${e}`);
}
log.info(`Downloading ${asset.name} to ${downloadBin}...`);
await github.download(asset, downloadBin, abort);
await fs.promises.chmod(downloadBin, 0o755);
// We await for the bufBinary to be set before returning and mutating the extension state.
const bufBinary = await getBufBinaryFromPath(downloadBin);
+ vscode.window.showInformationMessage(`Buf ${release.name} is now installed.`);
return bufBinary;
}
@@ -560,14 +420,10 @@ async function showPopup(message: string, url: string) {
* Returns an error if bufVersion is too low to run the LSP server.
*/
function getBufArgs() {
- const bufArgs = [];
- if (config.get("debug")) {
+ const bufArgs = ["--log-format", "text"];
+ if (config.get("debugLogs")) {
bufArgs.push("--debug");
}
- const logFormat = config.get("log-format");
- if (logFormat) {
- bufArgs.push("--log-format", logFormat);
- }
const bufVersion = bufState.getBufBinaryVersion();
let args = ["lsp", "serve"];
if (bufVersion?.compare(minBufVersion) === -1) {
diff --git a/src/status-bar.ts b/src/status-bar.ts
index a685cfe5..31a11eaa 100644
--- a/src/status-bar.ts
+++ b/src/status-bar.ts
@@ -1,6 +1,5 @@
import { effect } from "@preact/signals-core";
import * as vscode from "vscode";
-import { installBuf } from "./commands/install-buf";
import { showCommands } from "./commands/show-commands";
import { showOutput } from "./commands/show-output";
import { startLanguageServer } from "./commands/start-lsp";
@@ -54,12 +53,6 @@ const languageServerStatusConfig: Record<
command: startLanguageServer.name,
tooltip: "$(debug-restart) Restart language server",
},
- LANGUAGE_SERVER_NOT_INSTALLED: {
- icon: "$(circle-slash)",
- colour: new vscode.ThemeColor("statusBarItem.errorBackground"),
- command: installBuf.name,
- tooltip: "$(circle-slash) Buf not installed",
- },
};
const busyStatusConfig: StatusBarConfig = {
diff --git a/src/status.ts b/src/status.ts
index 1a929e69..d6520da7 100644
--- a/src/status.ts
+++ b/src/status.ts
@@ -12,7 +12,6 @@ const _languageServerStatus = [
"LANGUAGE_SERVER_RUNNING",
"LANGUAGE_SERVER_STOPPED",
"LANGUAGE_SERVER_ERRORED",
- "LANGUAGE_SERVER_NOT_INSTALLED",
] as const;
/**
diff --git a/test/integration/buf-binary.test.ts b/test/integration/buf-binary.test.ts
index 59189722..2bf1382f 100644
--- a/test/integration/buf-binary.test.ts
+++ b/test/integration/buf-binary.test.ts
@@ -1,95 +1,14 @@
import assert from "node:assert";
import * as cp from "node:child_process";
-import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
-import * as vscode from "vscode";
import { promisify } from "node:util";
import { effect } from "@preact/signals-core";
-import { HttpResponse, http } from "msw";
-import { setupServer } from "msw/node";
+import * as vscode from "vscode";
import which from "which";
-import { stopLanguageServer } from "../../src/commands/stop-lsp";
-import * as config from "../../src/config";
-import { githubReleaseURL, type Release } from "../../src/github";
import { bufState } from "../../src/state";
import type { LanguageServerStatus } from "../../src/status";
-
-/**
- * The test asset download URL. We use a single test download URL for all assets so that
- * tests are consistent across platforms.
- */
-const assetDownloadURL =
- "https://api.github.com/repos/bufbuild/buf/releases/assets/";
-const downloadBinPath = "test/workspaces/empty-single/node_modules/@bufbuild/";
-
-/**
- * msw stub handlers for GitHub releases API.
- */
-const handlers = [
- http.get(`${githubReleaseURL}tags/:tag`, ({ params }) => {
- if (typeof params.tag !== "string") {
- return HttpResponse.json({ error: params.tag }, { status: 404 });
- }
- return HttpResponse.json({
- name: params.tag,
- tag_name: params.tag,
- assets: [
- {
- name: "buf-Darwin-arm64",
- url: `${assetDownloadURL}buf-darwin-arm64`,
- },
- {
- name: "buf-Darwin-x86_64",
- url: `${assetDownloadURL}buf-darwin-x64`,
- },
- {
- name: "buf-Linux-x86_64",
- url: `${assetDownloadURL}buf-linux-x64`,
- },
- {
- name: "buf-Linux-aarch64",
- url: `${assetDownloadURL}buf-linux-aarch64`,
- },
- {
- name: "buf-Windows-x86_64.exe",
- url: `${assetDownloadURL}buf-win32-x64`,
- },
- {
- name: "buf-Windows-arm64.exe",
- url: `${assetDownloadURL}buf-win32-arm64`,
- },
- ],
- } satisfies Release);
- }),
- http.get(`${assetDownloadURL}:platformKey`, ({ params }) => {
- try {
- const bin = fs.readFileSync(
- os.platform() === "win32"
- ? path.resolve(
- __dirname,
- `../../../${downloadBinPath}${params.platformKey}/bin/buf.exe`
- )
- : `${downloadBinPath}${params.platformKey}/bin/buf`
- );
- const stream = new ReadableStream({
- start(controller) {
- controller.enqueue(bin);
- controller.close();
- },
- });
- return new HttpResponse(stream, {
- headers: {
- "content-type": "application/octet-stream",
- },
- });
- } catch (e) {
- return HttpResponse.json({ error: e }, { status: 404 });
- }
- }),
-];
-
-const server = setupServer(...handlers);
+import { server } from "../shared/shared";
/**
* Wraps {@link cp.exec} into an async call.
@@ -103,107 +22,45 @@ suite("manage buf binary and LSP", () => {
suiteTeardown(async () => {
server.close();
- await config.update("commandLine.path", undefined);
- await config.update("commandLine.version", undefined);
});
teardown(async () => {
- // Reset the state of the extension after each test case
- await stopLanguageServerForTest();
- if (config.get("commandLine.path")) {
- const languageServerRunning = setupLanguageServerListener(
- "LANGUAGE_SERVER_RUNNING"
- );
- await config.update("commandLine.path", undefined);
- await languageServerRunning;
- await stopLanguageServerForTest();
- }
- if (config.get("commandLine.version")) {
- const languageServerRunning = setupLanguageServerListener(
- "LANGUAGE_SERVER_RUNNING"
- );
- await config.update("commandLine.version", undefined);
- await languageServerRunning;
- await stopLanguageServerForTest();
- }
server.resetHandlers();
});
- test("no configs, use system buf on $PATH", async () => {
+ test(`setup buf ${process.env.BUF_INSTALLED}`, async () => {
const languageServerRunning = setupLanguageServerListener(
"LANGUAGE_SERVER_RUNNING"
);
- // Must activate the extension as part of the test
await vscode.extensions.getExtension("bufbuild.vscode-buf")?.activate();
await languageServerRunning;
- const { stdout, stderr } = await exec("buf --version");
- assert.strictEqual(stderr, "");
- const bufBinaryVersion = bufState.getBufBinaryVersion();
- assert.ok(bufBinaryVersion);
- assert.strictEqual(bufBinaryVersion.compare(stdout), 0);
- const bufFilename = os.platform() === "win32" ? "buf.exe" : "buf";
- const bufPath = await which(bufFilename, { nothrow: true });
- const installedBufBinaryPath = bufState.getBufBinaryPath();
- assert.ok(installedBufBinaryPath);
- assert.strictEqual(bufPath, installedBufBinaryPath);
- });
-
- test("configure commandLine.path", async () => {
- // Setup a listener for the language server status
- const languageServerRunning = setupLanguageServerListener(
- "LANGUAGE_SERVER_RUNNING"
- );
- let configPath = "node_modules/.bin/buf";
- if (os.platform() === "win32") {
- configPath = path.resolve(
- __dirname,
- `../../../test/workspaces/empty-single/node_modules/@bufbuild/buf-${os.platform()}-${os.arch()}/bin/buf.exe`
+ // This value is set in the GitHub Actions testing workflow
+ if (process.env.BUF_INSTALLED === "buf-on-path") {
+ // We expected buf to be installed on the system $PATH and for that to be used.
+ const { stdout, stderr } = await exec("buf --version");
+ assert.strictEqual(stderr, "");
+ const bufFilename = os.platform() === "win32" ? "buf.exe" : "buf";
+ const bufPath = await which(bufFilename, { nothrow: true });
+ const installedBufBinaryPath = bufState.getBufBinaryPath();
+ assert.ok(installedBufBinaryPath);
+ assert.strictEqual(bufPath, installedBufBinaryPath);
+ const bufBinaryVersion = bufState.getBufBinaryVersion();
+ assert.ok(bufBinaryVersion);
+ assert.strictEqual(bufBinaryVersion.compare(stdout), 0);
+ } else {
+ // We expect no buf CLI in the $PATH and the installation flow to trigger.
+ const bufBinaryPath = bufState.getBufBinaryPath();
+ assert.ok(bufBinaryPath);
+ assert.ok(
+ path.matchesGlob(
+ bufBinaryPath,
+ `**/.vscode-test/user-data/User/globalStorage/bufbuild.vscode-buf/v1.54.0/buf*`
+ )
);
}
- // Update the configuration to use a path for the buf binary. This will trigger a new
- // install process for the buf binary, which then starts the language server after.
- await config.update("commandLine.path", configPath);
- await languageServerRunning;
-
- // Assert the binary path is the configured path
- const bufBinaryPath = bufState.getBufBinaryPath();
- assert.ok(bufBinaryPath);
- assert.ok(bufBinaryPath.endsWith(configPath), bufBinaryPath);
- });
-
- test("configure commandLine.update", async () => {
- const languageServerRunning = setupLanguageServerListener(
- "LANGUAGE_SERVER_RUNNING"
- );
- const configuredVersion = "v1.54.0";
- await config.update("commandLine.version", configuredVersion);
- await languageServerRunning;
-
- // Assert the binary path used is the "downloaded" binary in global storage
- const bufBinaryPath = bufState.getBufBinaryPath();
- assert.ok(bufBinaryPath);
- assert.ok(
- path.matchesGlob(
- bufBinaryPath,
- `**/.vscode-test/user-data/User/globalStorage/bufbuild.vscode-buf/v1.54.0/buf*`
- ),
- bufBinaryPath
- );
});
});
-/**
- * A helper function for stopping the language server and ensuring the status is stable using
- * a listener.
- */
-async function stopLanguageServerForTest() {
- const languageServerStopped = setupLanguageServerListener(
- "LANGUAGE_SERVER_STOPPED"
- );
- await stopLanguageServer.execute();
- await languageServerStopped;
-}
-
/**
* A helper function that returns a Promise listening for the language server status. Once
* the language server is the status we want to listen for, the promise resolves. If the
@@ -219,10 +76,7 @@ function setupLanguageServerListener(
resolve();
dispose();
}
- if (
- languageServerStatus === "LANGUAGE_SERVER_NOT_INSTALLED" ||
- languageServerStatus === "LANGUAGE_SERVER_ERRORED"
- ) {
+ if (languageServerStatus === "LANGUAGE_SERVER_ERRORED") {
reject(
new Error(`language server in failed state: ${languageServerStatus}`)
);
diff --git a/test/playwright/extension.test.ts b/test/playwright/extension.test.ts
index 74670167..721e9b06 100644
--- a/test/playwright/extension.test.ts
+++ b/test/playwright/extension.test.ts
@@ -426,11 +426,6 @@ extensionTest.describe("command palette", async () => {
"Prune module dependencies.",
"Update module dependencies.",
"Generate",
- "List module files.",
- "Price for BSR paid plans.",
- "Module stats",
- "Install CLI",
- "Update CLI",
"Show Buf Output",
];
for (const expectation of expectations) {
diff --git a/test/playwright/global-setup.ts b/test/playwright/global-setup.ts
new file mode 100644
index 00000000..36db1413
--- /dev/null
+++ b/test/playwright/global-setup.ts
@@ -0,0 +1,8 @@
+import { server } from "../shared/shared";
+
+async function globalSetup() {
+ server.listen();
+ return () => server.close();
+}
+
+export default globalSetup;
diff --git a/test/shared/shared.ts b/test/shared/shared.ts
new file mode 100644
index 00000000..f0a26b7b
--- /dev/null
+++ b/test/shared/shared.ts
@@ -0,0 +1,81 @@
+import * as fs from "node:fs";
+import * as os from "node:os";
+import * as path from "node:path";
+import { HttpResponse, http } from "msw";
+import { setupServer } from "msw/node";
+
+/**
+ * The test asset download URL. We use a single test download URL for all assets so that
+ * tests are consistent across platforms.
+ *
+ * githubReleaseURL matches the value in src/github.ts.
+ */
+const assetDownloadURL =
+ "https://api.github.com/repos/bufbuild/buf/releases/assets/";
+const downloadBinPath = "test/workspaces/empty-single/node_modules/@bufbuild/";
+const githubReleaseURL = "https://api.github.com/repos/bufbuild/buf/releases/";
+
+/**
+ * msw stub handlers for GitHub releases API.
+ */
+const handlers = [
+ http.get(`${githubReleaseURL}latest`, () => {
+ return HttpResponse.json({
+ name: "v1.54.0",
+ tag_name: "v1.54.0",
+ assets: [
+ {
+ name: "buf-Darwin-arm64",
+ url: `${assetDownloadURL}buf-darwin-arm64`,
+ },
+ {
+ name: "buf-Darwin-x86_64",
+ url: `${assetDownloadURL}buf-darwin-x64`,
+ },
+ {
+ name: "buf-Linux-x86_64",
+ url: `${assetDownloadURL}buf-linux-x64`,
+ },
+ {
+ name: "buf-Linux-aarch64",
+ url: `${assetDownloadURL}buf-linux-aarch64`,
+ },
+ {
+ name: "buf-Windows-x86_64.exe",
+ url: `${assetDownloadURL}buf-win32-x64`,
+ },
+ {
+ name: "buf-Windows-arm64.exe",
+ url: `${assetDownloadURL}buf-win32-arm64`,
+ },
+ ],
+ });
+ }),
+ http.get(`${assetDownloadURL}:platformKey`, ({ params }) => {
+ try {
+ const bin = fs.readFileSync(
+ os.platform() === "win32"
+ ? path.resolve(
+ __dirname,
+ `../../../${downloadBinPath}${params.platformKey}/bin/buf.exe`
+ )
+ : `${downloadBinPath}${params.platformKey}/bin/buf`
+ );
+ const stream = new ReadableStream({
+ start(controller) {
+ controller.enqueue(bin);
+ controller.close();
+ },
+ });
+ return new HttpResponse(stream, {
+ headers: {
+ "content-type": "application/octet-stream",
+ },
+ });
+ } catch (e) {
+ return HttpResponse.json({ error: e }, { status: 404 });
+ }
+ }),
+];
+
+export const server = setupServer(...handlers);