diff --git a/hyperdb-bootstrap/CHANGELOG.md b/hyperdb-bootstrap/CHANGELOG.md index 4cd4044..42f2b79 100644 --- a/hyperdb-bootstrap/CHANGELOG.md +++ b/hyperdb-bootstrap/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +### Fixed + +- **Download `hyperd` from the Java API bundle instead of the C++ bundle.** + Tableau's C++ `macos-arm64` zip ships an **x86_64** `hyperd` (an upstream + packaging defect), so on Apple Silicon the extracted `hyperd` only ran + under Rosetta. The Java `macos-arm64` bundle carries a native arm64 + `hyperd`. The bundles share an identical URL template (only the + `java`/`cxx` token differs) and an identical internal layout + (`lib/hyper/hyperd`), so the switch is confined to the URL token and the + pinned per-platform sha256s in `hyperd-version.toml`. The other three + platforms (`macos-x86_64`, `linux-x86_64`, `windows-x86_64`) are + unaffected in architecture but now also come from the Java bundle for + consistency. + ## [0.1.1] - 2026-05-13 ### Added diff --git a/hyperdb-bootstrap/README.md b/hyperdb-bootstrap/README.md index 59f8a8a..9bd9bd2 100644 --- a/hyperdb-bootstrap/README.md +++ b/hyperdb-bootstrap/README.md @@ -1,15 +1,23 @@ # hyperdb-bootstrap -Download and install the `hyperd` executable from Tableau's Hyper C++ API +Download and install the `hyperd` executable from Tableau's Hyper Java API release packages. Ships as both a CLI binary and a library. The `hyperd` server isn't on crates.io — it's a prebuilt binary distributed -inside Tableau's Hyper C++ API zips at +inside Tableau's Hyper API zips at . This crate automates the "download the right zip for your platform, extract `hyperd` out of `lib/hyper/`, put it somewhere useful" step so contributors and CI can bootstrap with a single command. +> **Why the Java bundle, not C++?** Tableau publishes `hyperd` inside both +> the C++ and Java API zips. The C++ `macos-arm64` zip currently ships an +> **x86_64** `hyperd` (an upstream packaging defect), so on Apple Silicon it +> would only run under Rosetta. The Java `macos-arm64` zip carries a native +> arm64 `hyperd`. The two bundles are otherwise identical for our purposes +> (same URL template, same `lib/hyper/hyperd` layout), so this crate pulls +> from the Java bundle on every platform for consistency. + ## Install ```bash diff --git a/hyperdb-bootstrap/hyperd-version.toml b/hyperdb-bootstrap/hyperd-version.toml index 9fe4f90..5fa731f 100644 --- a/hyperdb-bootstrap/hyperd-version.toml +++ b/hyperdb-bootstrap/hyperd-version.toml @@ -1,4 +1,10 @@ -# Pinned Hyper C++ API release used by hyperd-bootstrap. +# Pinned Hyper **Java** API release used by hyperd-bootstrap. +# +# We pull `hyperd` from the Java binding's bundle, NOT the C++ one: the +# C++ macos-arm64 zip ships an x86_64 `hyperd` (upstream packaging defect), +# which only runs under Rosetta on Apple Silicon. The Java macos-arm64 +# bundle carries a native arm64 `hyperd`. Same URL template and same +# internal layout (lib/hyper/hyperd) — only the binding token + sha256s differ. # # Bump these values (and sha256s) when upgrading. Contributors without # an override get this exact release, so reproducibility depends on it. @@ -7,9 +13,9 @@ build_id = "r2bfd835b" # sha256 of each platform's .zip. Omit a platform to skip verification # for that platform (not recommended). Compute with: -# shasum -a 256 tableauhyperapi-cxx--release-main...zip +# shasum -a 256 tableauhyperapi-java--release-main...zip [sha256] -"macos-arm64" = "6cec0a90c7e8ddae2b0a623d551cdab4f085b0c6e4655713d6ce164ec20dabc7" -"macos-x86_64" = "d5d3dae60ce071aed45e534fb6a4fc6c7edce47e31d664d131af9f76d9b3a2aa" -"linux-x86_64" = "50f015ea2991afb9440fc93df0520c1b3a077dab609b010cbe1ce17685f44e20" -"windows-x86_64" = "918709daae78effa67bac804f8f6f45deea11056272577b73591590e11019f17" +"macos-arm64" = "2b0fa3fefcf4eba60f052e1cb51abfc32d8c84354274513763760f9549b45991" +"macos-x86_64" = "2fb7e58a449f5902e603f46ee7a73c70c85a2ef01e0c6435a26f893c2b9ee1f0" +"linux-x86_64" = "3d3fd2104f55f7fad832470592394dc78f350a03d52e89d36c5288b202dd0bc0" +"windows-x86_64" = "9dc4851d416e0e6e00f0367ee6b45fcd676e7ba3a110d4644e3bec871b9aa1de" diff --git a/hyperdb-bootstrap/src/extract.rs b/hyperdb-bootstrap/src/extract.rs index 3fc10a2..fb34978 100644 --- a/hyperdb-bootstrap/src/extract.rs +++ b/hyperdb-bootstrap/src/extract.rs @@ -1,14 +1,16 @@ // Copyright (c) 2026, Salesforce, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT -//! ZIP-archive extraction for the Hyper C++ API release bundle. +//! ZIP-archive extraction for the Hyper API release bundle. //! //! The upstream archive nests `hyperd` plus its shared libraries inside a //! versioned top-level directory (e.g. -//! `tableauhyperapi-cxx-macos-arm64-release-main.0.0.24457.rc36858b6/`) and +//! `tableauhyperapi-java-macos-arm64-release-main.0.0.24457.rc36858b6/`) and //! then under `lib/hyper/` on Linux/macOS or `bin/hyper/` on Windows. This //! module flattens both layers so downstream consumers only see the -//! `hyperd` runtime files. +//! `hyperd` runtime files. The layout is identical across the Java and C++ +//! bundles, so this extractor is agnostic to which binding we download +//! (we use Java — see `url.rs` for why). use std::fs::{self, File}; use std::io; @@ -17,7 +19,7 @@ use std::path::{Path, PathBuf}; use crate::Error; /// Extract everything under `lib/hyper/` (or `bin/hyper/` on Windows) from -/// the Hyper C++ API zip into `dest_dir`, flattening the wrapper prefixes +/// the Hyper API zip into `dest_dir`, flattening the wrapper prefixes /// away. Returns the list of extracted file paths relative to `dest_dir`. /// /// # Errors @@ -87,14 +89,14 @@ pub fn extract_hyperd(zip_path: &Path, dest_dir: &Path) -> Result, } /// Return the path stripped of a leading `lib/hyper/` or `bin/hyper/` prefix, -/// or `None` if the entry is outside those directories. The Hyper C++ API zip -/// wraps everything in a top-level `tableauhyperapi-cxx-...` directory and +/// or `None` if the entry is outside those directories. The Hyper API zip +/// wraps everything in a top-level `tableauhyperapi--...` directory and /// nests the runtime under `lib/hyper/` (Linux/macOS) or `bin/hyper/` /// (Windows) inside it. fn strip_lib_hyper_prefix(path: &Path) -> Option { let mut comps = path.components(); // Skip one optional top-level wrapper component (e.g. - // `tableauhyperapi-cxx-macos-arm64-release-main.0.0.24457.rc36858b6`) + // `tableauhyperapi-java-macos-arm64-release-main.0.0.24457.rc36858b6`) // before looking for the `lib/hyper` or `bin/hyper` pair. let first = comps.next()?; let (a, b) = if first.as_os_str() == "lib" || first.as_os_str() == "bin" { @@ -126,29 +128,29 @@ mod tests { ); // Real-world form: wrapped under a versioned top-level dir (Linux/macOS). assert_eq!( - strip_lib_hyper_prefix(Path::new("tableauhyperapi-cxx-x/lib/hyper/hyperd")), + strip_lib_hyper_prefix(Path::new("tableauhyperapi-java-x/lib/hyper/hyperd")), Some(PathBuf::from("hyperd")) ); assert_eq!( - strip_lib_hyper_prefix(Path::new("tableauhyperapi-cxx-x/lib/hyper/sub/a.so")), + strip_lib_hyper_prefix(Path::new("tableauhyperapi-java-x/lib/hyper/sub/a.so")), Some(PathBuf::from("sub/a.so")) ); // Windows uses bin/hyper/ instead of lib/hyper/. assert_eq!( - strip_lib_hyper_prefix(Path::new("tableauhyperapi-cxx-x/bin/hyper/hyperd.exe")), + strip_lib_hyper_prefix(Path::new("tableauhyperapi-java-x/bin/hyper/hyperd.exe")), Some(PathBuf::from("hyperd.exe")) ); assert_eq!( - strip_lib_hyper_prefix(Path::new("tableauhyperapi-cxx-x/bin/hyper/sub/extra.dll")), + strip_lib_hyper_prefix(Path::new("tableauhyperapi-java-x/bin/hyper/sub/extra.dll")), Some(PathBuf::from("sub/extra.dll")) ); // Anything outside lib/hyper or bin/hyper is dropped. assert_eq!( - strip_lib_hyper_prefix(Path::new("tableauhyperapi-cxx-x/include/foo.hpp")), + strip_lib_hyper_prefix(Path::new("tableauhyperapi-java-x/include/foo.hpp")), None ); assert_eq!( - strip_lib_hyper_prefix(Path::new("tableauhyperapi-cxx-x/bin/tableauhyperapi.dll")), + strip_lib_hyper_prefix(Path::new("tableauhyperapi-java-x/bin/tableauhyperapi.dll")), None ); assert_eq!(strip_lib_hyper_prefix(Path::new("other/file")), None); @@ -164,11 +166,11 @@ mod tests { let mut zw = zip::ZipWriter::new(file); let opts = zip::write::SimpleFileOptions::default(); // Mirror the real release zip's top-level wrapper dir. - zw.start_file("tableauhyperapi-cxx-fake/lib/hyper/hyperd", opts)?; + zw.start_file("tableauhyperapi-java-fake/lib/hyper/hyperd", opts)?; zw.write_all(b"fake hyperd")?; - zw.start_file("tableauhyperapi-cxx-fake/lib/hyper/sub/extra.txt", opts)?; + zw.start_file("tableauhyperapi-java-fake/lib/hyper/sub/extra.txt", opts)?; zw.write_all(b"extra")?; - zw.start_file("tableauhyperapi-cxx-fake/include/ignored.hpp", opts)?; + zw.start_file("tableauhyperapi-java-fake/include/ignored.hpp", opts)?; zw.write_all(b"nope")?; zw.finish()?; } diff --git a/hyperdb-bootstrap/src/install.rs b/hyperdb-bootstrap/src/install.rs index 3f77616..803764c 100644 --- a/hyperdb-bootstrap/src/install.rs +++ b/hyperdb-bootstrap/src/install.rs @@ -154,7 +154,7 @@ fn download_and_extract( let url = build_download_url(release, platform); let tmp = tempfile::tempdir().map_err(|source| Error::io("creating temp dir", source))?; - let zip_path = tmp.path().join("hyperapi-cxx.zip"); + let zip_path = tmp.path().join("hyperapi-java.zip"); download_and_verify(&url, release.sha256_for(platform), &zip_path)?; extract_hyperd(&zip_path, versioned_dir)?; Ok(()) diff --git a/hyperdb-bootstrap/src/lib.rs b/hyperdb-bootstrap/src/lib.rs index 2e3eb43..73b8529 100644 --- a/hyperdb-bootstrap/src/lib.rs +++ b/hyperdb-bootstrap/src/lib.rs @@ -1,8 +1,10 @@ // Copyright (c) 2026, Salesforce, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT -//! Download and install the `hyperd` executable from Tableau's Hyper C++ -//! API release packages. +//! Download and install the `hyperd` executable from Tableau's Hyper Java +//! API release packages. (The Java bundle is used rather than the C++ one +//! because the C++ `macos-arm64` zip ships an x86_64 `hyperd`; see the +//! `url` module for the full rationale.) //! //! The crate ships both a CLI binary (`hyperd-bootstrap`) and a small //! library. The library is blocking (no async runtime required) and has diff --git a/hyperdb-bootstrap/src/platform.rs b/hyperdb-bootstrap/src/platform.rs index 6ca3d93..4d718b7 100644 --- a/hyperdb-bootstrap/src/platform.rs +++ b/hyperdb-bootstrap/src/platform.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT //! Host-platform detection and slug encoding for the four targets that -//! Tableau's Hyper C++ API ships binaries for. +//! Tableau's Hyper API ships binaries for. use serde::{Deserialize, Serialize}; use std::fmt; diff --git a/hyperdb-bootstrap/src/scrape.rs b/hyperdb-bootstrap/src/scrape.rs index 98419a3..8350e58 100644 --- a/hyperdb-bootstrap/src/scrape.rs +++ b/hyperdb-bootstrap/src/scrape.rs @@ -20,7 +20,7 @@ use crate::Error; const RELEASES_URL: &str = "https://tableau.github.io/hyper-db/docs/releases"; /// Fetches the public releases page and returns the newest `PinnedRelease` -/// that has a C++ download for `platform`. +/// that has a Java download for `platform`. /// /// The returned `PinnedRelease` has an empty SHA-256 map — scraping only /// recovers the version + build id, never a digest. @@ -58,10 +58,10 @@ fn parse_latest(html: &str, platform: Platform) -> Result "no

VERSION [DATE]

heading found", ))?; - // For that version, find the C++ zip for the requested platform to + // For that version, find the Java zip for the requested platform to // recover the build id. let href_re = Regex::new(&format!( - r"tableauhyperapi-cxx-{plat}-release-main\.{ver}\.(rc[a-z0-9]+)\.zip", + r"tableauhyperapi-java-{plat}-release-main\.{ver}\.(rc[a-z0-9]+)\.zip", plat = regex::escape(platform.slug()), ver = regex::escape(&version), )) @@ -71,7 +71,7 @@ fn parse_latest(html: &str, platform: Platform) -> Result .and_then(|c| c.get(1)) .map(|m| m.as_str().to_string()) .ok_or(Error::ScrapeFailed( - "no matching cxx zip href for scraped version", + "no matching java zip href for scraped version", ))?; Ok(PinnedRelease { @@ -90,8 +90,8 @@ mod tests { let html = r#"

0.0.24457 [February 12 2026]

  • Release notes
- C++ (macOS arm64) - C++ (Linux) + Java (macOS arm64) + Java (Linux)

0.0.20000 [January 1 2025]

"#; let release = parse_latest(html, Platform::MacosArm64).unwrap(); diff --git a/hyperdb-bootstrap/src/url.rs b/hyperdb-bootstrap/src/url.rs index 4b5a553..60edd12 100644 --- a/hyperdb-bootstrap/src/url.rs +++ b/hyperdb-bootstrap/src/url.rs @@ -1,8 +1,16 @@ // Copyright (c) 2026, Salesforce, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 OR MIT -//! Builds canonical download URLs for Tableau's public Hyper C++ API +//! Builds canonical download URLs for Tableau's public Hyper **Java** API //! release bundles. +//! +//! We deliberately use the Java binding's bundle rather than the C++ one: +//! the C++ `macos-arm64` zip ships an **x86_64** `hyperd` (an upstream +//! packaging defect), so on Apple Silicon it would only run under Rosetta. +//! The Java `macos-arm64` bundle carries a native arm64 `hyperd`. Both +//! bundles share the identical URL template (only the `java`/`cxx` token +//! differs) and the identical internal layout (`lib/hyper/hyperd`), so the +//! switch is confined to this token and the pinned sha256s. use crate::platform::Platform; use crate::release::PinnedRelease; @@ -13,11 +21,11 @@ const BASE_URL: &str = "https://downloads.tableau.com/tssoftware"; /// combination. /// /// The URL template matches -/// `https://downloads.tableau.com/tssoftware/tableauhyperapi-cxx--release-main...zip`. +/// `https://downloads.tableau.com/tssoftware/tableauhyperapi-java--release-main...zip`. #[must_use] pub fn build_download_url(release: &PinnedRelease, platform: Platform) -> String { format!( - "{base}/tableauhyperapi-cxx-{plat}-release-main.{version}.{build_id}.zip", + "{base}/tableauhyperapi-java-{plat}-release-main.{version}.{build_id}.zip", base = BASE_URL, plat = platform.slug(), version = release.version, @@ -40,7 +48,7 @@ mod tests { let url = build_download_url(&r, Platform::MacosArm64); assert_eq!( url, - "https://downloads.tableau.com/tssoftware/tableauhyperapi-cxx-macos-arm64-release-main.0.0.24457.rc36858b6.zip" + "https://downloads.tableau.com/tssoftware/tableauhyperapi-java-macos-arm64-release-main.0.0.24457.rc36858b6.zip" ); } } diff --git a/hyperdb-bootstrap/tests/integration.rs b/hyperdb-bootstrap/tests/integration.rs index cd4d36a..9c989cb 100644 --- a/hyperdb-bootstrap/tests/integration.rs +++ b/hyperdb-bootstrap/tests/integration.rs @@ -12,7 +12,7 @@ fn builtin_release_builds_a_valid_url() { let r = PinnedRelease::builtin(); let url = build_download_url(&r, Platform::LinuxX86_64); assert!(url.starts_with("https://downloads.tableau.com/tssoftware/")); - assert!(url.contains("cxx-linux-x86_64")); + assert!(url.contains("java-linux-x86_64")); assert!(std::path::Path::new(&url) .extension() .is_some_and(|ext| ext.eq_ignore_ascii_case("zip")));