-
Notifications
You must be signed in to change notification settings - Fork 47
feat(bcode-serve): serve-only binary variant for headless containers #148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b8f651f
feat(bcode-serve): add serve-only binary variant for headless containers
sauravpanda cd05aae
refactor(install): serve the bytecode build from its own URL, leave i…
sauravpanda 91cd4ae
docs: trim comments, drop internal ticket refs and benchmark numbers
sauravpanda 2b2cf34
fix: address review — executable bit, musl assets, timer leak, instal…
sauravpanda 7c1664b
fix(install): stage the binary in the install dir, not the temp dir
sauravpanda b72e027
fix(release): don't publish serve assets built from the wrong commit
sauravpanda fe459c6
fix(bcode-serve): close web-UI import hole; harden installer and build
sauravpanda File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| #!/bin/sh | ||
| # | ||
| # BrowserCode installer — serve-only build. Hosted at https://bcode.sh/bytecode, | ||
| # alongside (not replacing) https://bcode.sh/install, which stays the path for | ||
| # the full CLI. | ||
| # | ||
| # curl -fsSL https://bcode.sh/bytecode | sh -s -- --no-modify-path --version 0.1.19 | ||
| # | ||
| # Installs `bcode-linux-<arch>[-musl]-serve`, which provides ONLY `bcode serve`; | ||
| # run, tui, web, github and the rest are absent and exit 1. It exists for headless | ||
| # containers. | ||
| # | ||
| # POSIX sh, not bash: Alpine is a supported target here and ships no bash. | ||
| set -eu | ||
|
|
||
| REPO=browser-use/browsercode | ||
| DIM='\033[0;2m' | ||
| RED='\033[0;31m' | ||
| OFF='\033[0m' | ||
|
|
||
| say() { printf "${DIM}%s${OFF}\n" "$1" >&2; } | ||
| die() { | ||
| printf "${RED}%s${OFF}\n" "$1" >&2 | ||
| shift | ||
| for line in "$@"; do [ -n "$line" ] && printf "${DIM}%s${OFF}\n" "$line" >&2; done | ||
| exit 1 | ||
| } | ||
|
|
||
| # Reject empty and flag-shaped values: `--version "$UNSET"` would otherwise | ||
| # silently install latest, which is exactly what pinning exists to prevent. | ||
| need() { | ||
| case $2 in | ||
| "" | -*) die "$1 needs a value (got: ${2:-<missing>})" ;; | ||
| esac | ||
| } | ||
|
|
||
| usage() { | ||
| cat >&2 <<EOF | ||
| BrowserCode installer (serve-only build) | ||
|
|
||
| -v, --version <ver> version to install (default: latest release) | ||
| --install-dir <d> where to install (default: \$HOME/.bcode/bin) | ||
| --no-modify-path accepted for install.sh parity; this script never edits | ||
| shell config files | ||
| -h, --help show this help | ||
|
|
||
| Installs a bcode that provides ONLY 'bcode serve'. | ||
| For the full CLI: curl -fsSL https://bcode.sh/install | bash | ||
| EOF | ||
| } | ||
|
|
||
| # Namespaced on purpose: a bare VERSION is a common Dockerfile ARG, and picking | ||
| # it up here would silently install the wrong build. | ||
| version=${BCODE_VERSION:-} | ||
| # HOME is routinely unset under `--user`/runAsUser with no passwd entry. | ||
| install_dir=${BCODE_INSTALL_DIR:-${HOME:-/root}/.bcode/bin} | ||
|
|
||
| while [ $# -gt 0 ]; do | ||
| case $1 in | ||
| -v | --version) need "--version" "${2:-}"; version=$2; shift 2 ;; | ||
| --install-dir) need "--install-dir" "${2:-}"; install_dir=$2; shift 2 ;; | ||
| --no-modify-path) shift ;; | ||
| # Tolerated: wrappers relaying args often forward an extra one. | ||
| --) shift ;; | ||
| -h | --help) usage; exit 0 ;; | ||
| # Fail rather than warn: a typo'd flag would otherwise quietly install | ||
| # "latest" into a build that meant to pin a version. | ||
| *) die "Unknown option: $1" "Run with --help for usage." ;; | ||
| esac | ||
| done | ||
|
|
||
| for tool in curl tar; do | ||
| command -v "$tool" >/dev/null 2>&1 || die "'$tool' is required but not installed." | ||
| done | ||
|
|
||
| [ "$(uname -s)" = Linux ] || die \ | ||
| "The serve build is published for linux only (got $(uname -s))." \ | ||
| "Use https://bcode.sh/install for the standard cross-platform binary." | ||
|
|
||
| case $(uname -m) in | ||
| aarch64 | arm64) arch=arm64 ;; | ||
| x86_64 | amd64) arch=x64 ;; | ||
| *) die "Unsupported architecture: $(uname -m)." "Supported: arm64, x64." ;; | ||
| esac | ||
|
|
||
| # Non-baseline x64 builds need AVX2 and no baseline serve asset is published. | ||
| # Only decide when /proc/cpuinfo actually carries a flags line: emulated or | ||
| # redacted cpuinfo (qemu `--platform linux/amd64` on arm64, lxcfs, some | ||
| # hypervisors) has none, and unknown is not the same as absent. | ||
| if [ "$arch" = x64 ] && grep -qi '^flags' /proc/cpuinfo 2>/dev/null && ! grep -qwi avx2 /proc/cpuinfo; then | ||
| die "This CPU has no AVX2 and no baseline serve build is published." \ | ||
| "Use https://bcode.sh/install, which ships a baseline binary." | ||
| fi | ||
|
|
||
| # A glibc binary cannot exec on musl. musl's ldd prints its banner and then exits | ||
| # non-zero, so match its output — the pipeline's status is grep's, not ldd's. | ||
| target=linux-$arch | ||
| if [ -f /etc/alpine-release ] || ldd --version 2>&1 | grep -qi musl; then | ||
| target=$target-musl | ||
| fi | ||
| asset=bcode-$target-serve.tar.gz | ||
|
|
||
| if [ -n "$version" ]; then | ||
| version=${version#v} | ||
| else | ||
| version=$(curl -fsSL "https://api.github.com/repos/$REPO/releases/latest" 2>/dev/null | | ||
| sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p') | ||
| [ -n "$version" ] || die "Could not resolve the latest version." "Pin one with --version <ver>." | ||
| fi | ||
| url=https://github.com/$REPO/releases/download/v$version/$asset | ||
|
|
||
| say "Installing bcode $version ($target, serve build)" | ||
|
|
||
| tmp=$(mktemp -d) | ||
| staged= | ||
| # `return 0` is load-bearing: the `&&` above it returns 1 whenever staged is | ||
| # empty, which under `set -e` would turn a clean run into a non-zero exit. | ||
| cleanup() { | ||
| rm -rf "$tmp" | ||
| [ -n "$staged" ] && rm -f "$staged" | ||
| return 0 | ||
| } | ||
| trap cleanup EXIT | ||
| # ash/dash do not run the EXIT trap on a signal; `docker build` cancellation | ||
| # sends TERM. Exiting from the handler routes through the EXIT trap above. | ||
| trap 'exit 130' INT | ||
| trap 'exit 143' TERM | ||
| trap 'exit 129' HUP | ||
|
|
||
| curl -fsSL -o "$tmp/$asset" "$url" || die \ | ||
| "Could not download $asset for v$version." \ | ||
| "URL: $url" \ | ||
| "Releases published before this variant existed do not carry the asset." | ||
|
|
||
| tar -xzf "$tmp/$asset" -C "$tmp" | ||
| [ -f "$tmp/bcode" ] || die "Archive did not contain a bcode binary." | ||
|
|
||
| # Stage inside the install dir, validate, then swap. Validating first means a bad | ||
| # download never replaces a working bcode; staging here rather than in /tmp avoids | ||
| # requiring an exec-capable /tmp (noexec there is common hardening) and makes the | ||
| # final step a same-filesystem rename, so the swap is atomic. | ||
| case $install_dir in | ||
| "") die "Install directory cannot be empty." ;; | ||
| -*) die "Install directory must not start with '-' (got: $install_dir)." ;; | ||
| esac | ||
| mkdir -p -- "$install_dir" | ||
| if [ -d "$install_dir/bcode" ]; then | ||
| die "$install_dir/bcode is a directory; refusing to install over it." | ||
| fi | ||
| staged=$(mktemp "$install_dir/.bcode.XXXXXX") | ||
| mv "$tmp/bcode" "$staged" | ||
| chmod 755 "$staged" | ||
|
|
||
| # Keep the binary's own stderr: on a libc mismatch it names the missing loader, | ||
| # which is the actual diagnosis. | ||
| if ! installed=$("$staged" --version 2>"$tmp/err"); then | ||
| die "Downloaded binary failed to run; existing install left untouched." \ | ||
| "$(cat "$tmp/err" 2>/dev/null)" \ | ||
| "If $install_dir is mounted noexec, pass --install-dir." | ||
| fi | ||
|
|
||
| mv "$staged" "$install_dir/bcode" | ||
| staged= | ||
|
|
||
| say "Installed $install_dir/bcode ($installed) — provides 'bcode serve' only" | ||
| case ":$PATH:" in | ||
| *":$install_dir:"*) ;; | ||
| *) say "Not on PATH. Add it with: export PATH=\"$install_dir:\$PATH\"" ;; | ||
| esac |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| { | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "version": "0.0.0", | ||
| "name": "@browser-use/bcode-serve", | ||
| "description": "Serve-only bcode binary variant for headless containers", | ||
| "type": "module", | ||
| "license": "MIT", | ||
| "private": true, | ||
| "scripts": { | ||
| "typecheck": "tsgo --noEmit", | ||
| "build": "bun run script/build.ts" | ||
| }, | ||
| "dependencies": { | ||
| "@browser-use/bcode-browser": "workspace:*", | ||
| "@browser-use/browsercode-core": "workspace:*", | ||
| "@opencode-ai/core": "workspace:*", | ||
| "@opencode-ai/script": "workspace:*", | ||
| "yargs": "18.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@tsconfig/bun": "catalog:", | ||
| "@types/bun": "catalog:", | ||
| "@types/yargs": "17.0.33" | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.