Skip to content

fix(cli): replace shell-based update with native Rust implementation#7148

Merged
jamadeo merged 2 commits intoblock:mainfrom
debugmcpdev:fix/native-rust-update-windows
Feb 18, 2026
Merged

fix(cli): replace shell-based update with native Rust implementation#7148
jamadeo merged 2 commits intoblock:mainfrom
debugmcpdev:fix/native-rust-update-windows

Conversation

@debugmcpdev
Copy link
Copy Markdown
Contributor

Summary

Replaces the shell-based goose update implementation (which shells out to curl + bash) with a native Rust implementation that works on Windows, macOS, and Linux.

Problem

goose update has been broken on native Windows since ~v1.9.0. The current implementation downloads download_cli.sh and executes it via bash - neither curl nor bash exist on native Windows. This affects all Windows users who try to update via the CLI.

See: #7146, #5006

Solution

Complete rewrite of crates/goose-cli/src/commands/update.rs (~37 lines -> ~460 lines) to use native Rust libraries instead of shell commands:

Step Before (broken) After (native)
Download curl reqwest (blocking + rustls)
Extract bash + tar zip crate (Windows) / tar + bzip2 (Unix)
Replace binary mv via bash std::fs::rename with rename-away strategy on Windows
DLL handling N/A Copies companion DLLs on Windows (libgcc, libstdc++, libwinpthread)

Key design decisions

  • Compile-time platform detection via #[cfg] attributes - no runtime OS sniffing
  • Rename-away binary replacement on Windows (goose.exe -> goose.exe.old, then new -> goose.exe) because Windows locks running executables
  • Handles goose-package/ subdirectory in archives (matches existing release structure)
  • Preserves existing API - update(canary, reconfigure) signature unchanged, just made async
  • disable-update feature flag still works

Files Changed

File Change
Cargo.lock Updated for new dependencies
crates/goose-cli/Cargo.toml Added reqwest (blocking, rustls-tls-native-roots), zip 7.4 (deflate), bzip2 0.5
crates/goose-cli/src/commands/update.rs Complete rewrite - native Rust download, extract, replace
crates/goose-cli/src/cli.rs One line: added .await to update() call
crates/goose-cli/src/session/editor.rs Added #[cfg(unix)] to 2 test functions (bonus fix for pre-existing Windows test compilation)

Testing

Unit tests (10 new, all pass)

  • test_asset_name_valid - correct platform asset selection
  • test_binary_name - correct binary name per OS
  • test_find_binary_* (4 tests) - binary discovery in various archive layouts
  • test_extract_zip_with_package_dir - zip extraction with goose-package subdir
  • test_replace_binary_* (3 tests) - binary replacement including Windows rename-away

End-to-end (manual, Windows)

  • goose update - successfully downloaded 50MB stable release, extracted, replaced binary + 3 DLLs
  • goose update --canary - successfully downloaded 89MB canary release
  • goose --version confirms updated version after each test

Regression

  • All 118 other goose-cli lib tests pass (0 regressions)

Closes

Replace the curl+bash update mechanism with a pure Rust implementation
that works on Windows, macOS, and Linux without requiring external tools.

The previous implementation downloaded and executed download_cli.sh via
curl and bash, which fails on native Windows where neither is available.
This has been broken since v1.9.0 (~4 months).

The new implementation:
- Uses reqwest to download the release archive directly
- Extracts .zip on Windows, .tar.bz2 on macOS/Linux
- Handles the goose-package/ subdirectory in archives
- Uses rename-away strategy on Windows (running exe can be renamed)
- Copies companion DLLs on Windows GNU builds
- Includes 10 unit tests covering all platform-specific logic

Also fixes editor.rs tests that failed to compile on Windows due to
unconditional use of std::os::unix.

Closes block#7146

Signed-off-by: JF <[email protected]>
@DOsinga
Copy link
Copy Markdown
Collaborator

DOsinga commented Feb 12, 2026

fixes: #7146

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Replaces the goose update command’s shell-based installer (curl/bash) with a native Rust implementation to support Windows/macOS/Linux updates reliably.

Changes:

  • Rewrote goose update to download release assets via reqwest, extract archives, and replace the running binary (with Windows-specific rename-away behavior + DLL copying).
  • Updated CLI dispatch to await the now-async updater.
  • Adjusted two editor tests to compile only on Unix.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
crates/goose-cli/src/commands/update.rs Native Rust updater: download, extract (zip/tar.bz2), locate binary, replace in-place, Windows DLL handling, and new unit tests.
crates/goose-cli/src/cli.rs Awaits the async update() command.
crates/goose-cli/Cargo.toml Adds dependencies needed for native download/extraction (reqwest/zip/bzip2).
crates/goose-cli/src/session/editor.rs Gates Unix-only symlink tests behind #[cfg(unix)].
Cargo.lock Lockfile updates for newly added dependencies.

#[cfg(all(target_os = "windows", target_arch = "x86_64"))]
{
"goose-x86_64-pc-windows-gnu.zip"
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asset_name() has no fallback for unsupported target triples, which can produce a confusing compile error on new/unsupported platforms; consider adding an explicit #[cfg(not(any(...)))] compile_error!(...) (or returning a clear error) so the failure mode is intentional and readable.

Suggested change
}
}
#[cfg(not(any(
all(target_os = "macos", target_arch = "aarch64"),
all(target_os = "macos", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "x86_64"),
all(target_os = "linux", target_arch = "aarch64"),
all(target_os = "windows", target_arch = "x86_64"),
)))]
{
compile_error!(
"goose update is not supported on this target; no prebuilt binary asset is available"
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +64
// --- Download -----------------------------------------------------------
let response = reqwest::get(&url)
.await
.context("Failed to download release archive")?;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The update download uses reqwest::get without any explicit timeout, so goose update can hang indefinitely on stalled networks; consider using a reqwest::Client with a reasonable request/overall timeout (and reuse it for the body download).

Copilot uses AI. Check for mistakes.
Comment on lines +144 to +148
let decoder = BzDecoder::new(data);
let mut archive = tar::Archive::new(decoder);
archive
.unpack(dest)
.context("Failed to extract tar.bz2 archive")?;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tar::Archive::unpack can write files with paths from the archive as-is; to avoid potential path traversal (writing outside dest) when extracting remote archives, consider iterating entries and using Entry::unpack_in(dest) (and rejecting absolute/parent components).

Copilot uses AI. Check for mistakes.
Comment on lines +165 to +169
if package_dir.is_dir() {
let p = package_dir.join(binary_name);
if p.exists() {
return Some(p);
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find_binary() uses Path::exists() which can match directories; using is_file() (and possibly metadata checks) avoids returning a non-file path that will later fail to copy/execute.

Copilot uses AI. Check for mistakes.
Comment on lines +268 to +283
if let Ok(entries) = fs::read_dir(source_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext.eq_ignore_ascii_case("dll") {
let file_name = path.file_name().unwrap();
let dest = dest_dir.join(file_name);
// Remove existing DLL first (it may be locked by another process)
if dest.exists() {
let _ = fs::remove_file(&dest);
}
fs::copy(&path, &dest).with_context(|| {
format!("Failed to copy {} to {}", path.display(), dest.display())
})?;
println!(" Copied {}", file_name.to_string_lossy());
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy_dlls() silently ignores read_dir failures via if let Ok(entries) = ..., which can report a successful update while skipping required DLL copies; consider propagating the error with context so update fails loudly when DLL enumeration isn’t possible.

Suggested change
if let Ok(entries) = fs::read_dir(source_dir) {
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext.eq_ignore_ascii_case("dll") {
let file_name = path.file_name().unwrap();
let dest = dest_dir.join(file_name);
// Remove existing DLL first (it may be locked by another process)
if dest.exists() {
let _ = fs::remove_file(&dest);
}
fs::copy(&path, &dest).with_context(|| {
format!("Failed to copy {} to {}", path.display(), dest.display())
})?;
println!(" Copied {}", file_name.to_string_lossy());
}
let entries = fs::read_dir(source_dir)
.with_context(|| format!("Failed to read directory {}", source_dir.display()))?;
for entry in entries.flatten() {
let path = entry.path();
if let Some(ext) = path.extension() {
if ext.eq_ignore_ascii_case("dll") {
let file_name = path.file_name().unwrap();
let dest = dest_dir.join(file_name);
// Remove existing DLL first (it may be locked by another process)
if dest.exists() {
let _ = fs::remove_file(&dest);
}
fs::copy(&path, &dest).with_context(|| {
format!("Failed to copy {} to {}", path.display(), dest.display())
})?;
println!(" Copied {}", file_name.to_string_lossy());

Copilot uses AI. Check for mistakes.
base64 = { workspace = true }
regex = "1.11.1"
tar = "0.4"
reqwest = { workspace = true, features = ["blocking", "rustls-tls-native-roots"] }
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reqwest is enabled with the blocking feature but the new updater uses the async API (reqwest::get(...).await); dropping the blocking feature would reduce dependency surface/compile time unless it’s needed elsewhere in goose-cli.

Suggested change
reqwest = { workspace = true, features = ["blocking", "rustls-tls-native-roots"] }
reqwest = { workspace = true, features = ["rustls-tls-native-roots"] }

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

@jamadeo jamadeo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, thanks for this. Tested this out on mac + windows

@jamadeo jamadeo added this pull request to the merge queue Feb 18, 2026
Merged via the queue into block:main with commit c932766 Feb 18, 2026
20 checks passed
michaelneale added a commit that referenced this pull request Feb 19, 2026
* main: (54 commits)
  docs: add monitoring subagent activity section (#7323)
  docs: document Desktop UI recipe editing for model/provider and extensions (#7327)
  docs: add CLAUDE_THINKING_BUDGET and CLAUDE_THINKING_ENABLED environm… (#7330)
  fix: display 'Code Mode' instead of 'code_execution' in CLI (#7321)
  docs: add Permission Policy documentation for MCP Apps (#7325)
  update RPI plan prompt (#7326)
  docs: add CLI syntax highlighting theme customization (#7324)
  fix(cli): replace shell-based update with native Rust implementation (#7148)
  docs: rename Code Execution extension to Code Mode extension (#7316)
  docs: remove ALPHA_FEATURES flag from documentation (#7315)
  docs: escape variable syntax in recipes (#7314)
  docs: update OTel environment variable and config guides (#7221)
  docs: system proxy settings (#7311)
  docs: add Summon extension tutorial and update Skills references (#7310)
  docs: agent session id (#7289)
  fix(gemini-cli): restore streaming lost in #7247 (#7291)
  Update more instructions (#7305)
  feat: add Moonshot and Kimi Code declarative providers (#7304)
  fix(cli): handle Reasoning content and fix streaming thinking display (#7296)
  feat: add GOOSE_SUBAGENT_MODEL and GOOSE_SUBAGENT_PROVIDER config options (#7277)
  ...
katzdave added a commit that referenced this pull request Feb 19, 2026
* 'main' of github.com:block/goose: (40 commits)
  Remove trailing space from links (#7156)
  fix: detect low balance and prompt for top up (#7166)
  feat(apps): add support for MCP apps to sample (#7039)
  Typescript SDK for ACP extension methods (#7319)
  chore: upgrade to rmcp 0.16.0 (#7274)
  docs: add monitoring subagent activity section (#7323)
  docs: document Desktop UI recipe editing for model/provider and extensions (#7327)
  docs: add CLAUDE_THINKING_BUDGET and CLAUDE_THINKING_ENABLED environm… (#7330)
  fix: display 'Code Mode' instead of 'code_execution' in CLI (#7321)
  docs: add Permission Policy documentation for MCP Apps (#7325)
  update RPI plan prompt (#7326)
  docs: add CLI syntax highlighting theme customization (#7324)
  fix(cli): replace shell-based update with native Rust implementation (#7148)
  docs: rename Code Execution extension to Code Mode extension (#7316)
  docs: remove ALPHA_FEATURES flag from documentation (#7315)
  docs: escape variable syntax in recipes (#7314)
  docs: update OTel environment variable and config guides (#7221)
  docs: system proxy settings (#7311)
  docs: add Summon extension tutorial and update Skills references (#7310)
  docs: agent session id (#7289)
  ...
jh-block added a commit that referenced this pull request Feb 19, 2026
* origin/main:
  fix(ci): deflake smoke tests for Google models (#7344)
  feat: add Cerebras provider support (#7339)
  fix: skip whitespace-only text blocks in Anthropic message (#7343)
  fix(goose-acp): heap allocations (#7322)
  Remove trailing space from links (#7156)
  fix: detect low balance and prompt for top up (#7166)
  feat(apps): add support for MCP apps to sample (#7039)
  Typescript SDK for ACP extension methods (#7319)
  chore: upgrade to rmcp 0.16.0 (#7274)
  docs: add monitoring subagent activity section (#7323)
  docs: document Desktop UI recipe editing for model/provider and extensions (#7327)
  docs: add CLAUDE_THINKING_BUDGET and CLAUDE_THINKING_ENABLED environm… (#7330)
  fix: display 'Code Mode' instead of 'code_execution' in CLI (#7321)
  docs: add Permission Policy documentation for MCP Apps (#7325)
  update RPI plan prompt (#7326)
  docs: add CLI syntax highlighting theme customization (#7324)
  fix(cli): replace shell-based update with native Rust implementation (#7148)
  docs: rename Code Execution extension to Code Mode extension (#7316)
aharvard added a commit that referenced this pull request Feb 19, 2026
* origin/main: (29 commits)
  fix(ci): deflake smoke tests for Google models (#7344)
  feat: add Cerebras provider support (#7339)
  fix: skip whitespace-only text blocks in Anthropic message (#7343)
  fix(goose-acp): heap allocations (#7322)
  Remove trailing space from links (#7156)
  fix: detect low balance and prompt for top up (#7166)
  feat(apps): add support for MCP apps to sample (#7039)
  Typescript SDK for ACP extension methods (#7319)
  chore: upgrade to rmcp 0.16.0 (#7274)
  docs: add monitoring subagent activity section (#7323)
  docs: document Desktop UI recipe editing for model/provider and extensions (#7327)
  docs: add CLAUDE_THINKING_BUDGET and CLAUDE_THINKING_ENABLED environm… (#7330)
  fix: display 'Code Mode' instead of 'code_execution' in CLI (#7321)
  docs: add Permission Policy documentation for MCP Apps (#7325)
  update RPI plan prompt (#7326)
  docs: add CLI syntax highlighting theme customization (#7324)
  fix(cli): replace shell-based update with native Rust implementation (#7148)
  docs: rename Code Execution extension to Code Mode extension (#7316)
  docs: remove ALPHA_FEATURES flag from documentation (#7315)
  docs: escape variable syntax in recipes (#7314)
  ...

# Conflicts:
#	ui/desktop/src/components/McpApps/McpAppRenderer.tsx
#	ui/desktop/src/components/McpApps/types.ts
katzdave added a commit that referenced this pull request Feb 19, 2026
* 'main' of github.com:block/goose: (24 commits)
  Docs: claude code uses stream-json (#7358)
  Improve link confirmation modal (#7333)
  fix(ci): deflake smoke tests for Google models (#7344)
  feat: add Cerebras provider support (#7339)
  fix: skip whitespace-only text blocks in Anthropic message (#7343)
  fix(goose-acp): heap allocations (#7322)
  Remove trailing space from links (#7156)
  fix: detect low balance and prompt for top up (#7166)
  feat(apps): add support for MCP apps to sample (#7039)
  Typescript SDK for ACP extension methods (#7319)
  chore: upgrade to rmcp 0.16.0 (#7274)
  docs: add monitoring subagent activity section (#7323)
  docs: document Desktop UI recipe editing for model/provider and extensions (#7327)
  docs: add CLAUDE_THINKING_BUDGET and CLAUDE_THINKING_ENABLED environm… (#7330)
  fix: display 'Code Mode' instead of 'code_execution' in CLI (#7321)
  docs: add Permission Policy documentation for MCP Apps (#7325)
  update RPI plan prompt (#7326)
  docs: add CLI syntax highlighting theme customization (#7324)
  fix(cli): replace shell-based update with native Rust implementation (#7148)
  docs: rename Code Execution extension to Code Mode extension (#7316)
  ...
tlongwell-block added a commit that referenced this pull request Feb 20, 2026
* origin/main: (62 commits)
  Docs: claude code uses stream-json (#7358)
  Improve link confirmation modal (#7333)
  fix(ci): deflake smoke tests for Google models (#7344)
  feat: add Cerebras provider support (#7339)
  fix: skip whitespace-only text blocks in Anthropic message (#7343)
  fix(goose-acp): heap allocations (#7322)
  Remove trailing space from links (#7156)
  fix: detect low balance and prompt for top up (#7166)
  feat(apps): add support for MCP apps to sample (#7039)
  Typescript SDK for ACP extension methods (#7319)
  chore: upgrade to rmcp 0.16.0 (#7274)
  docs: add monitoring subagent activity section (#7323)
  docs: document Desktop UI recipe editing for model/provider and extensions (#7327)
  docs: add CLAUDE_THINKING_BUDGET and CLAUDE_THINKING_ENABLED environm… (#7330)
  fix: display 'Code Mode' instead of 'code_execution' in CLI (#7321)
  docs: add Permission Policy documentation for MCP Apps (#7325)
  update RPI plan prompt (#7326)
  docs: add CLI syntax highlighting theme customization (#7324)
  fix(cli): replace shell-based update with native Rust implementation (#7148)
  docs: rename Code Execution extension to Code Mode extension (#7316)
  ...

# Conflicts:
#	crates/goose-server/src/main.rs
fresh3nough added a commit to fresh3nough/goose that referenced this pull request Mar 11, 2026
Replace custom SHA-256 checksum approach with Sigstore provenance verification
using the sigstore-verification crate. Leverages the SLSA attestations from
actions/attest-build-provenance added in block#7097 and builds on the native Rust
self-update foundation from block#7148.

Changes:
- Add verify_provenance() that fetches and verifies GitHub attestation bundles
  via the sigstore-verification crate (Sigstore signature chain, Rekor
  transparency log, artifact digest match)
- Harden tar.bz2 extraction against tar-slip (reject absolute paths and ..
  components, create parent dirs per-entry)
- Harden zip extraction against zip-slip (use enclosed_name() sanitization,
  iterate entries individually)
- Add sha256_hex() helper for archive digest computation and display
- Remove all .sha256 file download/verify logic and CI workflow changes
- 16 unit tests covering SHA-256 digests, path validation (safe paths,
  absolute path rejection, traversal rejection, nested traversal), tar
  extraction round-trip, binary location, binary replacement, and provenance
  verification graceful degradation

Signed-off-by: fre$h <[email protected]>
Signed-off-by: fre <[email protected]>
fresh3nough added a commit to fresh3nough/goose that referenced this pull request Mar 12, 2026
Replace custom SHA-256 checksum approach with Sigstore provenance verification
using the sigstore-verification crate. Leverages the SLSA attestations from
actions/attest-build-provenance added in block#7097 and builds on the native Rust
self-update foundation from block#7148.

Changes:
- Add verify_provenance() that fetches and verifies GitHub attestation bundles
  via the sigstore-verification crate (Sigstore signature chain, Rekor
  transparency log, artifact digest match)
- Harden tar.bz2 extraction against tar-slip (reject absolute paths and ..
  components, create parent dirs per-entry)
- Harden zip extraction against zip-slip (use enclosed_name() sanitization,
  iterate entries individually)
- Add sha256_hex() helper for archive digest computation and display
- Remove all .sha256 file download/verify logic and CI workflow changes
- 16 unit tests covering SHA-256 digests, path validation (safe paths,
  absolute path rejection, traversal rejection, nested traversal), tar
  extraction round-trip, binary location, binary replacement, and provenance
  verification graceful degradation

Signed-off-by: fre$h <[email protected]>
Signed-off-by: fre <[email protected]>
fresh3nough added a commit to fresh3nough/goose that referenced this pull request Mar 18, 2026
Replace custom SHA-256 checksum approach with Sigstore provenance verification
using the sigstore-verification crate. Leverages the SLSA attestations from
actions/attest-build-provenance added in block#7097 and builds on the native Rust
self-update foundation from block#7148.

Changes:
- Add verify_provenance() that fetches and verifies GitHub attestation bundles
  via the sigstore-verification crate (Sigstore signature chain, Rekor
  transparency log, artifact digest match)
- Harden tar.bz2 extraction against tar-slip (reject absolute paths and ..
  components, create parent dirs per-entry)
- Harden zip extraction against zip-slip (use enclosed_name() sanitization,
  iterate entries individually)
- Add sha256_hex() helper for archive digest computation and display
- Remove all .sha256 file download/verify logic and CI workflow changes
- 16 unit tests covering SHA-256 digests, path validation (safe paths,
  absolute path rejection, traversal rejection, nested traversal), tar
  extraction round-trip, binary location, binary replacement, and provenance
  verification graceful degradation

Signed-off-by: fre$h <[email protected]>
Signed-off-by: fre <[email protected]>
fresh3nough added a commit to fresh3nough/goose that referenced this pull request Mar 18, 2026
Replace custom SHA-256 checksum approach with Sigstore provenance verification
using the sigstore-verification crate. Leverages the SLSA attestations from
actions/attest-build-provenance added in block#7097 and builds on the native Rust
self-update foundation from block#7148.

Changes:
- Add verify_provenance() that fetches and verifies GitHub attestation bundles
  via the sigstore-verification crate (Sigstore signature chain, Rekor
  transparency log, artifact digest match)
- Harden tar.bz2 extraction against tar-slip (reject absolute paths and ..
  components, create parent dirs per-entry)
- Harden zip extraction against zip-slip (use enclosed_name() sanitization,
  iterate entries individually)
- Add sha256_hex() helper for archive digest computation and display
- Remove all .sha256 file download/verify logic and CI workflow changes
- 16 unit tests covering SHA-256 digests, path validation (safe paths,
  absolute path rejection, traversal rejection, nested traversal), tar
  extraction round-trip, binary location, binary replacement, and provenance
  verification graceful degradation

Signed-off-by: fre$h <[email protected]>
Signed-off-by: fre <[email protected]>
fresh3nough added a commit to fresh3nough/goose that referenced this pull request Mar 19, 2026
Replace custom SHA-256 checksum approach with Sigstore provenance verification
using the sigstore-verification crate. Leverages the SLSA attestations from
actions/attest-build-provenance added in block#7097 and builds on the native Rust
self-update foundation from block#7148.

Changes:
- Add verify_provenance() that fetches and verifies GitHub attestation bundles
  via the sigstore-verification crate (Sigstore signature chain, Rekor
  transparency log, artifact digest match)
- Harden tar.bz2 extraction against tar-slip (reject absolute paths and ..
  components, create parent dirs per-entry)
- Harden zip extraction against zip-slip (use enclosed_name() sanitization,
  iterate entries individually)
- Add sha256_hex() helper for archive digest computation and display
- Remove all .sha256 file download/verify logic and CI workflow changes
- 16 unit tests covering SHA-256 digests, path validation (safe paths,
  absolute path rejection, traversal rejection, nested traversal), tar
  extraction round-trip, binary location, binary replacement, and provenance
  verification graceful degradation

Signed-off-by: fre$h <[email protected]>
Signed-off-by: fre <[email protected]>
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.

5 participants