From cbb312e74f8ad74d425658ef5d70916907482ade Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 10 Apr 2026 23:26:09 +0300 Subject: [PATCH] Split binary and source install flows --- .github/workflows/release-edge.yml | 91 +++++++++++++++++++ README.md | 22 ++++- docs/deployment.md | 2 +- docs/install.md | 32 +++++-- scripts/install-source.sh | 107 ++++++++++++++++++++++ scripts/install.sh | 138 +++++++++++++---------------- scripts/local-rustfs-bootstrap.sh | 67 ++++++++++++-- 7 files changed, 367 insertions(+), 92 deletions(-) create mode 100644 .github/workflows/release-edge.yml create mode 100755 scripts/install-source.sh diff --git a/.github/workflows/release-edge.yml b/.github/workflows/release-edge.yml new file mode 100644 index 0000000..6c6a57e --- /dev/null +++ b/.github/workflows/release-edge.yml @@ -0,0 +1,91 @@ +name: Release Edge + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + prepare_edge_release: + name: Prepare edge release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout source + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Force-update edge tag + run: | + git tag -f edge "$GITHUB_SHA" + git push origin refs/tags/edge --force + + build_release: + name: Build edge ${{ matrix.asset_name }} + needs: prepare_edge_release + runs-on: ${{ matrix.runner }} + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + asset_name: omnigraph-linux-x86_64 + - runner: macos-13 + asset_name: omnigraph-macos-x86_64 + - runner: macos-14 + asset_name: omnigraph-macos-arm64 + env: + CARGO_TERM_COLOR: always + steps: + - name: Checkout source + uses: actions/checkout@v4 + + - name: Install Linux dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y protobuf-compiler libprotobuf-dev + + - name: Install macOS dependencies + if: runner.os == 'macOS' + run: brew install protobuf + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Cache Rust build data + uses: Swatinem/rust-cache@v2 + with: + workspaces: | + . -> target + + - name: Build release binaries + run: cargo build --release --locked -p omnigraph-cli -p omnigraph-server + + - name: Package release archive + run: | + mkdir -p release + install -m 0755 target/release/omnigraph release/omnigraph + install -m 0755 target/release/omnigraph-server release/omnigraph-server + tar -C release -czf "${{ matrix.asset_name }}.tar.gz" omnigraph omnigraph-server + shasum -a 256 "${{ matrix.asset_name }}.tar.gz" > "${{ matrix.asset_name }}.sha256" + + - name: Publish edge release assets + uses: softprops/action-gh-release@v2 + with: + tag_name: edge + name: Edge + prerelease: true + make_latest: false + body: | + Rolling prerelease from `${{ github.sha }}`. + files: | + ${{ matrix.asset_name }}.tar.gz + ${{ matrix.asset_name }}.sha256 diff --git a/README.md b/README.md index 70dcc82..ae184c2 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,22 @@ workflows, and storage that runs equally well on a local directory or an curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | bash ``` -This installs `omnigraph` and `omnigraph-server` into `~/.local/bin`. If no -tagged release exists for your platform yet, the installer falls back to a -source build. +This installs `omnigraph` and `omnigraph-server` into `~/.local/bin` from +published release binaries. It does not compile from source. + +If the stable channel is not published yet, use the `edge` channel below. + +To install the rolling prerelease binaries from `main` instead: + +```bash +curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | RELEASE_CHANNEL=edge bash +``` + +If you want a source build, use: + +```bash +curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install-source.sh | bash +``` ## One-Command Local RustFS Bootstrap @@ -30,6 +43,9 @@ That bootstrap: Docker must be installed and running first. +The RustFS bootstrap prefers the rolling `edge` binaries and only falls back to +source builds when release assets are unavailable. + ## Good Fit For - Team knowledge graphs and internal context graphs diff --git a/docs/deployment.md b/docs/deployment.md index 73d82f2..4f01295 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -52,7 +52,7 @@ The bootstrap: Supported behavior: -- downloads a tagged release binary when one exists for the current platform +- downloads the rolling `edge` binary when one exists for the current platform - otherwise clones `ModernRelay/omnigraph-public` and builds from source - reuses an existing RustFS container if it is already running diff --git a/docs/install.md b/docs/install.md index 841f526..c8b2b72 100644 --- a/docs/install.md +++ b/docs/install.md @@ -13,9 +13,29 @@ By default the installer places: in `~/.local/bin`. -If a matching release asset exists for your platform, the installer downloads -and unpacks it. Otherwise it falls back to cloning `ModernRelay/omnigraph-public` -and building from source. +The default installer is binary-only. It downloads a published release asset, +verifies the SHA256 checksum, and unpacks it. It does not build from source. +If the stable channel is not published yet, use the `edge` channel below. + +## Channels + +Stable binaries: + +```bash +curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | bash +``` + +Rolling edge binaries from `main`: + +```bash +curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | RELEASE_CHANNEL=edge bash +``` + +Install from source: + +```bash +curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install-source.sh | bash +``` ## Useful Overrides @@ -25,16 +45,16 @@ Install to a different directory: curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | INSTALL_DIR="$HOME/bin" bash ``` -Force a source build even if a release asset exists: +Install a specific tag: ```bash -curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | FORCE_BUILD=1 bash +curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | VERSION=v0.1.0 bash ``` Build from a specific git ref: ```bash -curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install.sh | SOURCE_REF=main bash +curl -fsSL https://raw.githubusercontent.com/ModernRelay/omnigraph-public/main/scripts/install-source.sh | SOURCE_REF=main bash ``` ## Manual Source Build diff --git a/scripts/install-source.sh b/scripts/install-source.sh new file mode 100755 index 0000000..7390587 --- /dev/null +++ b/scripts/install-source.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +set -euo pipefail + +REPO_SLUG="${REPO_SLUG:-ModernRelay/omnigraph-public}" +SOURCE_REF="${SOURCE_REF:-main}" +INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}" +TMP_ROOT="${TMPDIR:-/tmp}" +WORKDIR="" + +log() { + printf '==> %s\n' "$*" +} + +die() { + printf 'error: %s\n' "$*" >&2 + exit 1 +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || die "missing required command: $1" +} + +cleanup() { + if [ -n "${WORKDIR:-}" ] && [ -d "$WORKDIR" ]; then + rm -rf "$WORKDIR" + fi +} + +trap cleanup EXIT + +repo_root_from_shell() { + if [ -f "$PWD/Cargo.toml" ] && [ -d "$PWD/crates" ]; then + printf '%s\n' "$PWD" + return 0 + fi + + if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then + local candidate + candidate="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + if [ -f "$candidate/Cargo.toml" ] && [ -d "$candidate/crates" ]; then + printf '%s\n' "$candidate" + return 0 + fi + fi + + return 1 +} + +install_from_dir() { + mkdir -p "$INSTALL_DIR" + install -m 0755 "$1/omnigraph" "$INSTALL_DIR/omnigraph" + install -m 0755 "$1/omnigraph-server" "$INSTALL_DIR/omnigraph-server" +} + +build_from_source() { + local repo_root + repo_root="${1:-}" + + if [ -z "$repo_root" ]; then + need_cmd git + WORKDIR="$(mktemp -d "$TMP_ROOT/omnigraph-install.XXXXXX")" + repo_root="$WORKDIR/source" + log "Cloning $REPO_SLUG at $SOURCE_REF" + git clone --depth 1 --branch "$SOURCE_REF" "https://github.com/$REPO_SLUG.git" "$repo_root" + fi + + need_cmd cargo + log "Building omnigraph binaries from source" + ( + cd "$repo_root" + cargo build --release --locked -p omnigraph-cli -p omnigraph-server + ) + + install_from_dir "$repo_root/target/release" +} + +print_summary() { + cat </dev/null 2>&1 || die "missing required command: $1" -} - cleanup() { if [ -n "${WORKDIR:-}" ] && [ -d "$WORKDIR" ]; then rm -rf "$WORKDIR" @@ -29,30 +25,6 @@ cleanup() { trap cleanup EXIT -repo_root_from_shell() { - if [ -f "$PWD/Cargo.toml" ] && [ -d "$PWD/crates" ]; then - printf '%s\n' "$PWD" - return 0 - fi - - if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then - local candidate - candidate="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" - if [ -f "$candidate/Cargo.toml" ] && [ -d "$candidate/crates" ]; then - printf '%s\n' "$candidate" - return 0 - fi - fi - - return 1 -} - -latest_release_tag() { - local json - json="$(curl -fsSL "https://api.github.com/repos/$REPO_SLUG/releases/latest" 2>/dev/null || true)" - printf '%s' "$json" | sed -n 's/.*"tag_name":[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1 -} - platform_asset_name() { local os arch os="$(uname -s)" @@ -74,56 +46,79 @@ platform_asset_name() { esac } +checksum_command() { + if command -v shasum >/dev/null 2>&1; then + printf 'shasum -a 256' + return + fi + + if command -v sha256sum >/dev/null 2>&1; then + printf 'sha256sum' + return + fi + + die "missing checksum tool: expected shasum or sha256sum" +} + +release_base_url() { + if [ -n "$VERSION" ]; then + printf 'https://github.com/%s/releases/download/%s\n' "$REPO_SLUG" "$VERSION" + return + fi + + case "$RELEASE_CHANNEL" in + stable) + printf 'https://github.com/%s/releases/latest/download\n' "$REPO_SLUG" + ;; + edge) + printf 'https://github.com/%s/releases/download/edge\n' "$REPO_SLUG" + ;; + *) + die "unsupported RELEASE_CHANNEL '$RELEASE_CHANNEL' (expected stable or edge)" + ;; + esac +} + install_from_dir() { mkdir -p "$INSTALL_DIR" install -m 0755 "$1/omnigraph" "$INSTALL_DIR/omnigraph" install -m 0755 "$1/omnigraph-server" "$INSTALL_DIR/omnigraph-server" } -install_from_release() { - local tag asset archive +verify_checksum() { + local archive="$1" + local checksum_file="$2" + local expected actual tool - [ "$FORCE_BUILD" = "1" ] && return 1 + expected="$(awk '{print $1}' "$checksum_file")" + [ -n "$expected" ] || die "checksum file did not contain a SHA256 digest" - tag="$(latest_release_tag)" - [ -n "$tag" ] || return 1 + tool="$(checksum_command)" + actual="$($tool "$archive" | awk '{print $1}')" - asset="$(platform_asset_name)" || return 1 - WORKDIR="$(mktemp -d "$TMP_ROOT/omnigraph-install.XXXXXX")" - archive="$WORKDIR/$asset" - - log "Downloading $asset from $tag" - curl -fsSL \ - "https://github.com/$REPO_SLUG/releases/download/$tag/$asset" \ - -o "$archive" || return 1 - - tar -C "$WORKDIR" -xzf "$archive" || return 1 - install_from_dir "$WORKDIR" - return 0 + [ "$actual" = "$expected" ] || die "checksum verification failed for $(basename "$archive")" } -build_from_source() { - local repo_root - repo_root="${1:-}" +install_from_release() { + local asset archive checksum base_url - if [ -z "$repo_root" ]; then - need_cmd git - need_cmd cargo + asset="$(platform_asset_name)" || die "no prebuilt binary is available for $(uname -s)/$(uname -m)" + WORKDIR="$(mktemp -d "$TMP_ROOT/omnigraph-install.XXXXXX")" + archive="$WORKDIR/$asset" + checksum="$WORKDIR/$asset.sha256" + base_url="$(release_base_url)" - WORKDIR="$(mktemp -d "$TMP_ROOT/omnigraph-install.XXXXXX")" - repo_root="$WORKDIR/source" - log "Cloning $REPO_SLUG at $SOURCE_REF" - git clone --depth 1 --branch "$SOURCE_REF" "https://github.com/$REPO_SLUG.git" "$repo_root" - fi + log "Downloading $asset" + curl -fsSL \ + "$base_url/$asset" \ + -o "$archive" || die "no published binary found for $asset; use scripts/install-source.sh or build from source" + curl -fsSL \ + "$base_url/$asset.sha256" \ + -o "$checksum" || die "checksum file for $asset was not found" - need_cmd cargo - log "Building omnigraph binaries from source" - ( - cd "$repo_root" - cargo build --release --locked -p omnigraph-cli -p omnigraph-server - ) - - install_from_dir "$repo_root/target/release" + verify_checksum "$archive" "$checksum" + tar -C "$WORKDIR" -xzf "$archive" || die "failed to unpack $asset" + install_from_dir "$WORKDIR" } print_summary() { @@ -149,15 +144,8 @@ EOF } main() { - local repo_root - - need_cmd curl - repo_root="$(repo_root_from_shell || true)" - - if ! install_from_release; then - build_from_source "$repo_root" - fi - + command -v curl >/dev/null 2>&1 || die "missing required command: curl" + install_from_release print_summary } diff --git a/scripts/local-rustfs-bootstrap.sh b/scripts/local-rustfs-bootstrap.sh index bda3f5e..4858d98 100755 --- a/scripts/local-rustfs-bootstrap.sh +++ b/scripts/local-rustfs-bootstrap.sh @@ -3,6 +3,7 @@ set -euo pipefail REPO_SLUG="${REPO_SLUG:-ModernRelay/omnigraph-public}" SOURCE_REF="${SOURCE_REF:-main}" +RELEASE_CHANNEL="${RELEASE_CHANNEL:-edge}" WORKDIR="${WORKDIR:-$PWD/.omnigraph-rustfs-demo}" RUSTFS_CONTAINER_NAME="${RUSTFS_CONTAINER_NAME:-omnigraph-rustfs-demo}" RUSTFS_IMAGE="${RUSTFS_IMAGE:-rustfs/rustfs:latest}" @@ -84,6 +85,48 @@ platform_asset_name() { esac } +checksum_command() { + if command -v shasum >/dev/null 2>&1; then + printf 'shasum -a 256' + return + fi + + if command -v sha256sum >/dev/null 2>&1; then + printf 'sha256sum' + return + fi + + die "missing checksum tool: expected shasum or sha256sum" +} + +release_base_url() { + case "$RELEASE_CHANNEL" in + stable) + printf 'https://github.com/%s/releases/latest/download\n' "$REPO_SLUG" + ;; + edge) + printf 'https://github.com/%s/releases/download/edge\n' "$REPO_SLUG" + ;; + *) + die "unsupported RELEASE_CHANNEL '$RELEASE_CHANNEL' (expected stable or edge)" + ;; + esac +} + +verify_checksum() { + local archive="$1" + local checksum_file="$2" + local expected actual tool + + expected="$(awk '{print $1}' "$checksum_file")" + [ -n "$expected" ] || die "checksum file did not contain a SHA256 digest" + + tool="$(checksum_command)" + actual="$($tool "$archive" | awk '{print $1}')" + + [ "$actual" = "$expected" ] || die "checksum verification failed for $(basename "$archive")" +} + ensure_aws_cli() { if command -v aws >/dev/null 2>&1; then AWS_BIN="$(command -v aws)" @@ -119,26 +162,36 @@ download_fixture_files() { } download_release_binaries() { - local tag asset archive_dir archive_path + local asset archive_dir archive_path checksum_path base_url [ "$FORCE_BUILD" = "1" ] && return 1 - tag="$(latest_release_tag)" - [ -n "$tag" ] || return 1 - asset="$(platform_asset_name)" || return 1 archive_dir="$WORKDIR/release" archive_path="$archive_dir/$asset" + checksum_path="$archive_dir/$asset.sha256" mkdir -p "$archive_dir" "$WORKDIR/bin" + base_url="$(release_base_url)" - log "Downloading release asset $asset from $tag" + log "Downloading release asset $asset" curl -fsSL \ - "https://github.com/$REPO_SLUG/releases/download/$tag/$asset" \ + "$base_url/$asset" \ -o "$archive_path" || return 1 + curl -fsSL \ + "$base_url/$asset.sha256" \ + -o "$checksum_path" || return 1 + verify_checksum "$archive_path" "$checksum_path" || return 1 tar -C "$WORKDIR/bin" -xzf "$archive_path" || return 1 BIN_DIR="$WORKDIR/bin" - download_fixture_files "$tag" || return 1 + if [ "$RELEASE_CHANNEL" = "stable" ]; then + local tag + tag="$(latest_release_tag)" + [ -n "$tag" ] || return 1 + download_fixture_files "$tag" || return 1 + else + download_fixture_files "main" || return 1 + fi } build_from_source() {