openvibers/openvibe.link Commits

Every system: Windows ARM64, Linux .deb/.rpm/tarballs and macOS tarballs on each release (#134)

OP OpenVibers committed 5 days ago · b8a097b493 · 6 files, +293 −16

* Every system: Windows ARM64, Linux .deb/.rpm/tarballs and macOS tarballs on each release

CI builds the release for Windows x64 and ARM64, macOS Apple silicon and Intel, and Linux x64 and ARM64. Each Linux
and macOS release now also carries a tarball with the installer and plugins, and each Linux architecture gets a .deb
and a .rpm that install openvibe-link with openvibe-node as its alias. The release workflow checks every asset is
present before publishing and uploads to an existing release, so the Android APK job and this one can both attach to
the same tag. CI builds and inspects the packages on every PR, so a broken .deb or .rpm is caught before a tag.

* Packages: make a CI build's version start with a digit

dpkg refuses a version that does not start with a digit, and CI builds are version 'ci'. Prefix 0. so the package
checks run on every PR; a released v0.45.0 is unchanged.

Changes

ddiff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
iindex 8618030..80345af 100644
---- a/.github/workflows/ci.yml
++++ b/.github/workflows/ci.yml
@@@ -82,9 +82,41 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
++ # The packages are built on every run so a broken .deb/.rpm is caught before a tag: dpkg-deb ships with Ubuntu,
++ # rpm (rpmbuild) does not.
++ - run: sudo apt-get update -qq && sudo apt-get install -y -qq rpm
- run: scripts/dist.sh ci
# OpenVibe.Bot's /install route and BOT_INSTALLER_SOURCE_URL are documented to serve this exact release asset.
- run: test -s dist/install.sh && cmp -s install/install.sh dist/install.sh
++ - name: every OS, architecture and package the release promises is in dist
++ run: |
++ for f in \
++ dist/openvibe-link-linux-amd64 dist/openvibe-link-linux-arm64 \
++ dist/openvibe-link-darwin-amd64 dist/openvibe-link-darwin-arm64 \
++ dist/openvibe-link-windows-amd64.exe dist/openvibe-link-windows-arm64.exe \
++ dist/openvibe-link_*_amd64.deb dist/openvibe-link_*_arm64.deb \
++ dist/openvibe-link-*-1.x86_64.rpm dist/openvibe-link-*-1.aarch64.rpm \
++ dist/openvibe-link_*_linux_amd64.tar.gz dist/openvibe-link_*_linux_arm64.tar.gz \
++ dist/openvibe-link_*_darwin_amd64.tar.gz dist/openvibe-link_*_darwin_arm64.tar.gz; do
++ test -s "$f" || { echo "missing $f"; exit 1; }
++ done
++ - name: the packages carry the program and the alias, not just a file
++ run: |
++ for f in dist/*.deb; do
++ dpkg-deb -c "$f" > /tmp/pkg.txt
++ grep -q 'usr/local/bin/openvibe-link$' /tmp/pkg.txt
++ grep -q 'usr/local/bin/openvibe-node -> openvibe-link$' /tmp/pkg.txt
++ done
++ for f in dist/*.rpm; do
++ rpm -qpl "$f" > /tmp/pkg.txt
++ grep -qx '/usr/local/bin/openvibe-link' /tmp/pkg.txt
++ grep -qx '/usr/local/bin/openvibe-node' /tmp/pkg.txt
++ done
++ for f in dist/openvibe-link_*.tar.gz; do
++ tar -tzf "$f" > /tmp/pkg.txt
++ grep -q '/install.sh$' /tmp/pkg.txt
++ grep -q '/README.md$' /tmp/pkg.txt
++ done
- run: ls -l dist && cat dist/SHA256SUMS
go-other-os:
ddiff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
iindex 740b852..b732deb 100644
---- a/.github/workflows/release.yml
++++ b/.github/workflows/release.yml
@@@ -24,21 +24,34 @@ jobs:
- run: go vet ./... && go test -count=1 ./...
- run: |
for p in sdk dryrun adeept_adr036 cozmo relay ros2 computer voice; do python -m pytest -q plugins/$p/tests; done
++ # The packages are built on every tag, and CI already built them on the PR that made this commit: rpm is the
++ # only tool Ubuntu does not ship.
++ - run: sudo apt-get update -qq && sudo apt-get install -y -qq rpm
- run: scripts/dist.sh "${GITHUB_REF_NAME}"
# Every release must carry the installer OpenVibe.Bot's GET /install route and BOT_INSTALLER_SOURCE_URL are
-- # documented to serve (releases/latest/download/install.sh), the plugin bundle the installer downloads, and the
-- # checksums it verifies them against.
-- - name: the release carries the assets the installer and OpenVibe.Bot depend on
++ # documented to serve (releases/latest/download/install.sh), the plugin bundle the installer downloads, every
++ # OS/architecture the site offers, and the checksums they are verified against.
++ - name: the release carries every OS, architecture and package the site offers
run: |
test -s dist/install.sh
test -s dist/openvibe-node-plugins.tar.gz
test -s dist/SHA256SUMS
-- # The new name and the alias it replaces are both in every release for a year (plan 2026-11-07 §5).
-- test -s dist/openvibe-link-linux-amd64
-- test -s dist/openvibe-node-linux-amd64
-- test -s dist/openvibe-link-windows-amd64.exe
-- test -s dist/openvibe-node-windows-amd64.exe
++ for f in \
++ dist/openvibe-link-linux-amd64 dist/openvibe-link-linux-arm64 \
++ dist/openvibe-link-darwin-amd64 dist/openvibe-link-darwin-arm64 \
++ dist/openvibe-link-windows-amd64.exe dist/openvibe-link-windows-arm64.exe \
++ dist/openvibe-link_*_amd64.deb dist/openvibe-link_*_arm64.deb \
++ dist/openvibe-link-*-1.x86_64.rpm dist/openvibe-link-*-1.aarch64.rpm \
++ dist/openvibe-link_*_linux_amd64.tar.gz dist/openvibe-link_*_linux_arm64.tar.gz \
++ dist/openvibe-link_*_darwin_amd64.tar.gz dist/openvibe-link_*_darwin_arm64.tar.gz \
++ dist/openvibe-node-linux-amd64 dist/openvibe-node-windows-amd64.exe; do
++ test -s "$f" || { echo "missing $f"; exit 1; }
++ done
- name: publish the release
env:
GH_TOKEN: ${{ github.token }}
-- run: gh release create "${GITHUB_REF_NAME}" dist/* --title "OpenVibe Link ${GITHUB_REF_NAME}" --generate-notes
++ # The Android workflow also attaches its APK on a tag, and either job may reach the release first: create it
++ # only when it is absent, then upload (clobber) so both workflows can publish to the same tag.
++ run: |
++ gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1 || gh release create "${GITHUB_REF_NAME}" --title "OpenVibe Link ${GITHUB_REF_NAME}" --generate-notes
++ gh release upload "${GITHUB_REF_NAME}" dist/* --clobber
ddiff --git a/README.md b/README.md
iindex 845ca0c..0a13b25 100644
---- a/README.md
++++ b/README.md
@@@ -8,6 +8,11 @@ One service for your computers, servers, Raspberry Pis, phones and robots: pair
like **OpenVibe.Bot** (robots: drive, cameras, sensors, with safety) and **OpenVibe.Actor** (computer control for AI
agents, at the level you set on the machine) can use the device. It connects out, needs no open ports, and keeps a local kill switch.
++Every release is built by CI for every system: **Windows x64 and ARM64**, **macOS Apple silicon and Intel**, **Linux
++x64 and ARM64** (`.deb`, `.rpm`, a tarball, and the `curl | sh` script), and the **Android APK**. The
++[openvibe.link install page](https://openvibe.link/#install) picks the visitor's OS and offers the others; the
++[install guide](docs/install.md) has the exact steps, including the home-screen web app for iPhone and iPad.
++
On a robot's Raspberry Pi (or other Linux computer), the one command:
```sh
ddiff --git a/docs/install.md b/docs/install.md
iindex 26214b1..c7c1f61 100644
---- a/docs/install.md
++++ b/docs/install.md
@@@ -3,16 +3,101 @@
OpenVibe Link is one program, `openvibe-link` (`openvibe-node` is kept as an alias), plus Python plugins for robots. It connects **out** to OpenVibe (no open
ports, works behind any home router), and a local kill switch stops everything it drives.
++
++## Install on your OS
++
++Every release is built by CI on the tag and carries the same program for each system: Windows x64 and ARM64, macOS
++Apple silicon and Intel, Linux x64 and ARM64 (.deb, .rpm, tarball and the `curl | sh` script), and the Android APK.
++Links for every file are on [openvibe.link](https://openvibe.link/#install); the release page has the same files and
++`SHA256SUMS`.
++
++### Windows (x64 and ARM64)
++
++1. Download `openvibe-link-windows-amd64.exe` (x64) or `openvibe-link-windows-arm64.exe` (ARM64) from
++ [openvibe.link](https://openvibe.link/#install) or the latest release.
++2. Windows may show SmartScreen the first time: **More info** → **Run anyway**.
++3. In an **Administrator** PowerShell, from the folder you saved it in:
++
++ ```powershell
++ .\openvibe-link pair <CODE>
++ .\openvibe-link install
++ ```
++
++ The code comes from [openvibe.bot/machines](https://openvibe.bot/machines) (**Add a machine**).
++4. `openvibe-link status` shows the link; the machine appears in your account's machines.
++
++`openvibe-node.exe` is the same program under its old name, so an existing shortcut or script keeps working.
++
++### macOS (Apple silicon and Intel)
++
++The one command installs the right build and the launchd service:
++
++```sh
++curl -fsSL https://openvibe.link/install | sh
++```
++
++Or download the program: `openvibe-link-darwin-arm64` (Apple silicon) or `openvibe-link-darwin-amd64` (Intel), or the
++tarball `openvibe-link_<version>_darwin_arm64.tar.gz` / `..._darwin_amd64.tar.gz` with `install.sh` and the plugins
++beside it. Gatekeeper: right-click the file and choose **Open** the first time. Then:
++
++```sh
++sudo ./openvibe-link pair <CODE>
++sudo ./openvibe-link install
++```
++
++The same command works on a Raspberry Pi or another Linux robot and also finds the kit and its driver.
++
++### Linux (x64 and ARM64: .deb, .rpm, tarball, script)
++
++The one command, on any distribution, finds the CPU and installs the systemd service:
++
++```sh
++curl -fsSL https://openvibe.link/install | sh
++```
++
++Or use the package for your distribution:
++
++```sh
++sudo apt install ./openvibe-link_<version>_amd64.deb # Debian, Ubuntu (x64; _arm64.deb on ARM64)
++sudo dnf install ./openvibe-link-<version>-1.x86_64.rpm # Fedora, RHEL (aarch64 on ARM64)
++```
++
++The packages put `openvibe-link` in `/usr/local/bin` and make `openvibe-node` a symlink to it; they do not start or
++pair anything by themselves. Then:
++
++```sh
++openvibe-link pair <CODE>
++sudo openvibe-link install
++```
++
++The tarballs (`openvibe-link_<version>_linux_amd64.tar.gz` and `..._linux_arm64.tar.gz`) carry the program, the
++plugin bundle and `install.sh`; unpack one and run `sudo ./install.sh --local .`.
++
++### Android (the existing APK)
++
++Download `openvibe-link-<tag>.apk` on the phone, allow installing from that source when Android asks, and open
++**OpenVibe Link**. Pair with a code from [openvibe.bot/link/phone](https://openvibe.bot/link/phone). This is the direct
++APK, not Google Play; it has approvals, Activity, pairing and **Send to OpenVibe**, and no phone automation. Details:
++[android.md](android.md).
++
++### iPhone and iPad (what works today)
++
++There is no native iOS app yet. The home-screen web app works now: open [openvibe.my](https://openvibe.my/) in Safari,
++sign in, then **Share** → **Add to Home Screen**. It gives you **Approvals**, **Activity**, your **machines** and
++pairing from the phone.
++
++A native iOS app, push notifications and a background Link connection wait on an Apple Developer account and the App
++Store review that follows it; nothing here pretends they already work.
++
## The one command (Raspberry Pi and other Linux robots)
On the robot's computer, with nothing set up on openvibe.bot first:
```sh
--curl -fsSL https://node.openvibe.network/install | sh
++curl -fsSL https://openvibe.link/install | sh
```
--The same installer is also at <https://openvibe.bot/install>, which keeps working; `node.openvibe.network` is the
--Node's own address and has a page that says what it is and how to install it.
++The same installer is also at <https://openvibe.bot/install>, which keeps working; `node.openvibe.network/install` keeps working and sends you to openvibe.link.
On a Raspberry Pi it turns on I2C and SPI (now and after a reboot, through `raspi-config`), installs what is missing of
`i2c-tools`, the camera tools (`rpicam-apps`), `python3-venv`, `python3-lgpio` and `qrencode`, and looks at the I2C bus:
@@@ -48,7 +133,7 @@ write Raspberry Pi OS Lite (64-bit) with Raspberry Pi Imager, copy the page's `u
card's `bootfs` drive, and switch the robot on. On first boot the Pi joins your Wi-Fi, turns on SSH, and runs
```sh
--curl -fsSL https://node.openvibe.network/install | sh -s -- --setup osk_…
++curl -fsSL https://openvibe.link/install | sh -s -- --setup osk_…
```
with the card's setup key (`OPENVIBE_SETUP_KEY` works too). openvibe.bot approves that claim at once for the person who
@@@ -63,7 +148,7 @@ Its output is in `journalctl -u openvibe-setup` on the Pi.
2. On the device, run that command, adding `--driver` for your robot:
```sh
-- curl -fsSL https://node.openvibe.network/install | sh -s -- --robot rob_01J8Z4M2Q0R7T9YV3K6N8P1W2X --code ABCD-1234 --driver adeept
++ curl -fsSL https://openvibe.link/install | sh -s -- --robot rob_01J8Z4M2Q0R7T9YV3K6N8P1W2X --code ABCD-1234 --driver adeept
```
The shape is `--robot rob_… --code CODE [--driver adeept|adeept-mecanum|cozmo|looi|ros2|ros2-holonomic|none]`. `--robot` is the robot's
@@@ -94,7 +179,7 @@ has the details.
- **Network pairing**: `--network <URL> --pairing pair_… --code CODE`, e.g.
```sh
-- curl -fsSL https://node.openvibe.network/install | sh -s -- --network https://openvibe.network --pairing pair_01J8Z4M2Q0R7T9YV3K6N8P1W2Y --code ABCD-1234 --driver adeept
++ curl -fsSL https://openvibe.link/install | sh -s -- --network https://openvibe.network --pairing pair_01J8Z4M2Q0R7T9YV3K6N8P1W2Y --code ABCD-1234 --driver adeept
```
The Node redeems the code on OpenVibe.Network (`POST <network>/api/v1/node-pairing`) and stores the machine's node
ddiff --git a/scripts/dist.sh b/scripts/dist.sh
iindex d8ea8ae..b0e35b1 100755
---- a/scripts/dist.sh
++++ b/scripts/dist.sh
@@@ -6,7 +6,7 @@ VERSION="${1:-$(git describe --tags --always --dirty 2>/dev/null || echo dev)}"
cd "$(dirname "$0")/.."
rm -rf dist
mkdir -p dist
--for target in linux/amd64 linux/arm64 linux/arm/7 darwin/amd64 darwin/arm64 windows/amd64; do
++for target in linux/amd64 linux/arm64 linux/arm/7 darwin/amd64 darwin/arm64 windows/amd64 windows/arm64; do
os="${target%%/*}"
rest="${target#*/}"
arch="${rest%%/*}"
@@@ -32,5 +32,7 @@ tar -czf dist/openvibe-node-plugins.tar.gz -C plugins \
# OpenVibe.Bot's GET /install route and BOT_INSTALLER_SOURCE_URL are documented to serve this asset name:
# https://github.com/OpenVibers/OpenVibe.Node/releases/latest/download/install.sh — do not rename it.
cp install/install.sh dist/install.sh
++# Friendly Linux and macOS artifacts: .deb, .rpm and a tarball per architecture, from the binaries above.
++scripts/package.sh "$VERSION"
(cd dist && if command -v sha256sum >/dev/null 2>&1; then sha256sum -- * >SHA256SUMS; else shasum -a 256 -- * >SHA256SUMS; fi)
echo "dist/ ready for $VERSION"
ddiff --git a/scripts/package.sh b/scripts/package.sh
nnew file mode 100755
iindex 0000000..cd468e8
---- /dev/null
++++ b/scripts/package.sh
@@@ -0,0 +1,140 @@
++#!/bin/sh
++# Build the friendly Linux and macOS artifacts from the binaries dist.sh just built:
++# .deb, .rpm and a .tar.gz for Linux amd64 and arm64, and a .tar.gz for macOS arm64 and amd64.
++# The binaries themselves stay in dist/ for the curl | sh installer and for direct download.
++# scripts/package.sh VERSION
++set -eu
++VERSION="${1:?usage: scripts/package.sh VERSION}"
++cd "$(dirname "$0")/.."
++
++# Package versions cannot carry a leading v or arbitrary git describe text: keep RPM and dpkg happy, and keep the
++# version visible to a person. v0.45.0 -> 0.45.0; v0.0.0-dev -> 0.0.0.dev.
++pkgver="${VERSION#v}"
++pkgver="$(printf '%s' "$pkgver" | sed 's/[^A-Za-z0-9.+~]/./g')"
++[ -n "$pkgver" ] || pkgver=0.0.0
++# dpkg refuses a version that does not start with a digit; the CI build is version "ci".
++case "$pkgver" in [0-9]*) ;; *) pkgver="0.$pkgver" ;; esac
++
++work="$(mktemp -d)"
++trap 'rm -rf "$work"' EXIT
++
++# docs every package and tarball carries next to the program
++docs() {
++ dir="$1"
++ mkdir -p "$dir"
++ cp README.md "$dir/README.md"
++ cp docs/install.md "$dir/install.md"
++}
++
++# ---- tarballs: Linux and macOS, one per architecture ----
++for target in linux/amd64 linux/arm64 darwin/amd64 darwin/arm64; do
++ os="${target%%/*}"
++ arch="${target##*/}"
++ case "$os/$arch" in
++ linux/amd64|linux/arm64) bin="dist/openvibe-link-linux-$arch" ;;
++ darwin/amd64|darwin/arm64) bin="dist/openvibe-link-darwin-$arch" ;;
++ *) echo "package: no tarball rule for $target" >&2; exit 1 ;;
++ esac
++ test -s "$bin" || { echo "package: $bin is missing (run scripts/dist.sh first)" >&2; exit 1; }
++
++ root="$work/openvibe-link-$pkgver-$os-$arch"
++ mkdir -p "$root"
++ # The installer's --local layout: the OS-specific binary name and the plugin bundle beside install.sh.
++ cp "$bin" "$root/openvibe-link-$os-$arch"
++ cp "$bin" "$root/openvibe-node-$os-$arch"
++ cp dist/openvibe-node-plugins.tar.gz "$root/"
++ cp install/install.sh "$root/install.sh"
++ docs "$root"
++ tar -czf "dist/openvibe-link_${pkgver}_${os}_${arch}.tar.gz" -C "$work" "openvibe-link-$pkgver-$os-$arch"
++ echo "built dist/openvibe-link_${pkgver}_${os}_${arch}.tar.gz"
++done
++
++# ---- .deb: the program and the old-name alias, plus docs; no scripts, no network in postinst ----
++for arch in amd64 arm64; do
++ bin="dist/openvibe-link-linux-$arch"
++ test -s "$bin" || { echo "package: $bin is missing" >&2; exit 1; }
++ root="$work/deb-$arch"
++ mkdir -p "$root/DEBIAN" "$root/usr/local/bin" "$root/usr/share/doc/openvibe-link"
++ install -m 0755 "$bin" "$root/usr/local/bin/openvibe-link"
++ ln -s openvibe-link "$root/usr/local/bin/openvibe-node"
++ cp README.md "$root/usr/share/doc/openvibe-link/README.md"
++ cp docs/install.md "$root/usr/share/doc/openvibe-link/install.md"
++ size="$(du -sk "$root/usr" | cut -f1)"
++ cat >"$root/DEBIAN/control" <<EOF
++Package: openvibe-link
++Version: $pkgver
++Section: net
++Priority: optional
++Architecture: $arch
++Maintainer: OpenVibe <[email protected]>
++Installed-Size: $size
++Depends: ca-certificates
++Homepage: https://openvibe.link
++Description: OpenVibe Link: your machines, connected
++ OpenVibe Link runs on your computer or server and connects out to OpenVibe: your agents can use it only as far as
++ you allow, and you choose what it offers the network. The openvibe-node command is kept as an alias.
++EOF
++ ( cd "$root" && find . -type f -exec md5sum {} + | sed 's| \./| |' | sort -k2 >DEBIAN/md5sums )
++ dpkg-deb --build --root-owner-group "$root" "dist/openvibe-link_${pkgver}_${arch}.deb" >/dev/null
++ echo "built dist/openvibe-link_${pkgver}_${arch}.deb"
++done
++
++# ---- .rpm: the same contents, built with rpmbuild; CI installs rpm, a local run skips it with a warning ----
++if command -v rpmbuild >/dev/null 2>&1; then
++ for pair in amd64:x86_64 arm64:aarch64; do
++ arch="${pair%%:*}"
++ rpmarch="${pair##*:}"
++ bin="dist/openvibe-link-linux-$arch"
++ test -s "$bin" || { echo "package: $bin is missing" >&2; exit 1; }
++ top="$work/rpm-$rpmarch"
++ mkdir -p "$top/SOURCES" "$top/SPECS" "$top/BUILD" "$top/BUILDROOT" "$top/RPMS" "$top/SRPMS"
++ cp "$bin" "$top/SOURCES/openvibe-link"
++ cp README.md "$top/SOURCES/README.md"
++ cp docs/install.md "$top/SOURCES/install.md"
++ cat >"$top/SPECS/openvibe-link.spec" <<EOF
++Name: openvibe-link
++Version: $pkgver
++Release: 1
++Summary: OpenVibe Link: your machines, connected
++License: AGPL-3.0
++URL: https://openvibe.link
++Source0: openvibe-link
++Source1: README.md
++Source2: install.md
++AutoReqProv: no
++%global debug_package %{nil}
++%global _build_id_links none
++
++%description
++OpenVibe Link runs on your computer or server and connects out to OpenVibe: your agents
++can use it only as far as you allow, and you choose what it offers the network. The
++openvibe-node command is kept as an alias.
++
++%prep
++
++%build
++
++%install
++mkdir -p %{buildroot}/usr/local/bin %{buildroot}/usr/share/doc/openvibe-link
++install -m 0755 %{SOURCE0} %{buildroot}/usr/local/bin/openvibe-link
++ln -s openvibe-link %{buildroot}/usr/local/bin/openvibe-node
++install -m 0644 %{SOURCE1} %{buildroot}/usr/share/doc/openvibe-link/README.md
++install -m 0644 %{SOURCE2} %{buildroot}/usr/share/doc/openvibe-link/install.md
++
++%files
++/usr/local/bin/openvibe-link
++/usr/local/bin/openvibe-node
++/usr/share/doc/openvibe-link/README.md
++/usr/share/doc/openvibe-link/install.md
++EOF
++ rpmbuild -bb --quiet --define "_topdir $top" --define "_arch $rpmarch" --define "_build_arch $rpmarch" \
++ --define "_target_cpu $rpmarch" --define "_target_os linux" "$top/SPECS/openvibe-link.spec" \
++ >/dev/null 2>"$work/rpm-$rpmarch.err" || { cat "$work/rpm-$rpmarch.err" >&2; exit 1; }
++ cp "$top"/RPMS/"$rpmarch"/*.rpm "dist/openvibe-link-${pkgver}-1.${rpmarch}.rpm"
++ echo "built dist/openvibe-link-${pkgver}-1.${rpmarch}.rpm"
++ done
++else
++ echo "package: rpmbuild is not installed here; skipped the .rpm (CI installs rpm and builds it)" >&2
++fi
++
++echo "packages ready for $VERSION"