From 48e5b97db995291a63559733b0ee9f5be477146f Mon Sep 17 00:00:00 2001 From: paoloanzn Date: Sun, 22 Mar 2026 23:14:11 +0100 Subject: [PATCH 1/2] docs: add release install instructions --- README.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5bb19a5..7cd1836 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,26 @@ The implementation is intentionally biased toward agent use: ## Install -Build the binary from the repository root: +One-liner installer for Linux and macOS: + +```bash +curl -fsSL https://raw.githubusercontent.com/Gladium-AI/flare-edge-cli/main/install.sh | sh +``` + +The installer detects your OS and CPU architecture, downloads the latest GitHub release archive, and installs `flare-edge-cli` into a user-local bin directory. It prefers `INSTALL_DIR` when set, then `XDG_BIN_HOME`, then `~/.local/bin`, then `~/bin`. + +Prebuilt release archives are attached automatically for: + +- `linux/amd64` +- `linux/arm64` +- `darwin/amd64` +- `darwin/arm64` +- `windows/amd64` +- `windows/arm64` + +Release downloads are published on the [GitHub Releases](https://github.com/Gladium-AI/flare-edge-cli/releases) page. + +Build from source from the repository root: ```bash make build From b3037cb9526f71fead3954d1b7261328502e1969 Mon Sep 17 00:00:00 2001 From: paoloanzn Date: Sun, 22 Mar 2026 23:13:47 +0100 Subject: [PATCH 2/2] ci: add release assets workflow and installer --- .github/workflows/release.yml | 123 +++++++++++++++++++++++++++++++++ install.sh | 124 ++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100755 install.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c0f43b8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,123 @@ +name: Release + +on: + release: + types: + - published + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: release-${{ github.workflow }}-${{ github.event.release.tag_name || github.ref }} + cancel-in-progress: false + +jobs: + build: + name: Build Release Artifacts + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + include: + - goos: linux + goarch: amd64 + archive_ext: tar.gz + - goos: linux + goarch: arm64 + archive_ext: tar.gz + - goos: darwin + goarch: amd64 + archive_ext: tar.gz + - goos: darwin + goarch: arm64 + archive_ext: tar.gz + - goos: windows + goarch: amd64 + archive_ext: zip + - goos: windows + goarch: arm64 + archive_ext: zip + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: go.mod + + - name: Build archive + shell: bash + env: + BINARY: flare-edge-cli + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + ARCHIVE_EXT: ${{ matrix.archive_ext }} + TAG: ${{ github.event.release.tag_name || github.ref_name }} + run: | + set -euo pipefail + + version="${TAG#refs/tags/}" + staging_dir="dist/${BINARY}_${version}_${GOOS}_${GOARCH}" + archive_base="${BINARY}_${version}_${GOOS}_${GOARCH}" + binary_name="${BINARY}" + + if [ "${GOOS}" = "windows" ]; then + binary_name="${binary_name}.exe" + fi + + mkdir -p "${staging_dir}" + GOOS="${GOOS}" GOARCH="${GOARCH}" CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o "${staging_dir}/${binary_name}" ./cmd/flare-edge-cli + cp LICENSE README.md "${staging_dir}/" + + pushd dist >/dev/null + if [ "${ARCHIVE_EXT}" = "zip" ]; then + zip -rq "${archive_base}.zip" "${archive_base}" + else + tar -czf "${archive_base}.tar.gz" "${archive_base}" + fi + popd >/dev/null + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: release-${{ matrix.goos }}-${{ matrix.goarch }} + path: | + dist/*.tar.gz + dist/*.zip + if-no-files-found: error + + publish: + name: Publish Release Assets + needs: build + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Download artifacts + uses: actions/download-artifact@v5 + with: + path: dist + pattern: release-* + merge-multiple: true + + - name: Generate checksums + shell: bash + run: | + set -euo pipefail + cd dist + shasum -a 256 *.tar.gz *.zip > checksums.txt + + - name: Upload release assets + uses: softprops/action-gh-release@v2 + with: + files: | + dist/*.tar.gz + dist/*.zip + dist/checksums.txt diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..1703ffd --- /dev/null +++ b/install.sh @@ -0,0 +1,124 @@ +#!/bin/sh +set -eu + +REPO="${REPO:-Gladium-AI/flare-edge-cli}" +BINARY="${BINARY:-flare-edge-cli}" + +resolve_install_dir() { + if [ -n "${INSTALL_DIR:-}" ]; then + printf '%s\n' "${INSTALL_DIR}" + return + fi + + if [ -n "${XDG_BIN_HOME:-}" ]; then + printf '%s\n' "${XDG_BIN_HOME}" + return + fi + + if [ -d "${HOME}/.local/bin" ]; then + printf '%s\n' "${HOME}/.local/bin" + return + fi + + if [ -d "${HOME}/bin" ]; then + printf '%s\n' "${HOME}/bin" + return + fi + + printf '%s\n' "${HOME}/.local/bin" +} + +detect_platform() { + case "$(uname -s)" in + Linux*) + GOOS="linux" + ;; + Darwin*) + GOOS="darwin" + ;; + *) + echo "Unsupported OS: $(uname -s)" >&2 + exit 1 + ;; + esac + + case "$(uname -m)" in + x86_64|amd64) + GOARCH="amd64" + ;; + arm64|aarch64) + GOARCH="arm64" + ;; + *) + echo "Unsupported architecture: $(uname -m)" >&2 + exit 1 + ;; + esac +} + +fetch_latest_tag() { + api_url="https://api.github.com/repos/${REPO}/releases/latest" + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "${api_url}" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n 1 + return + fi + + if command -v wget >/dev/null 2>&1; then + wget -qO- "${api_url}" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n 1 + return + fi + + echo "Error: curl or wget is required" >&2 + exit 1 +} + +download_archive() { + url="$1" + out="$2" + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "${url}" -o "${out}" + return + fi + + wget -q "${url}" -O "${out}" +} + +INSTALL_DIR="$(resolve_install_dir)" +detect_platform + +echo "Detected platform: ${GOOS}/${GOARCH}" + +TAG="${TAG:-$(fetch_latest_tag)}" +if [ -z "${TAG}" ]; then + echo "Error: could not determine latest release tag" >&2 + exit 1 +fi + +ARCHIVE="${BINARY}_${TAG}_${GOOS}_${GOARCH}.tar.gz" +URL="https://github.com/${REPO}/releases/download/${TAG}/${ARCHIVE}" +TMPDIR="$(mktemp -d)" +trap 'rm -rf "${TMPDIR}"' EXIT INT TERM + +echo "Latest release: ${TAG}" +echo "Downloading ${URL}" +download_archive "${URL}" "${TMPDIR}/${ARCHIVE}" + +tar -xzf "${TMPDIR}/${ARCHIVE}" -C "${TMPDIR}" +EXTRACTED_DIR="${TMPDIR}/${BINARY}_${TAG}_${GOOS}_${GOARCH}" + +mkdir -p "${INSTALL_DIR}" +install -m 0755 "${EXTRACTED_DIR}/${BINARY}" "${INSTALL_DIR}/${BINARY}" + +echo "Installed ${BINARY} ${TAG} to ${INSTALL_DIR}/${BINARY}" + +case ":${PATH}:" in + *":${INSTALL_DIR}:"*) + ;; + *) + echo + echo "Add ${INSTALL_DIR} to your PATH:" + echo " export PATH=\"${INSTALL_DIR}:\$PATH\"" + ;; +esac