mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
Feat:package manager channels (brew, apt, npm ) (#166)
* feat(release): upload versioned CLI tarballs in release workflow (#128) Package and upload non-Windows CLI tarballs with SHA-256 checksum companions during release builds so package managers can consume stable release artifacts. Made-with: Cursor * feat(packaging): add brew apt npm release channels automation (#128) Add post-release workflow and channel assets to publish Homebrew formula updates, signed apt repository metadata, and npm package releases with smoke validation. Made-with: Cursor * docs: add installation guide for OpenHuman across various package managers
This commit is contained in:
@@ -0,0 +1,454 @@
|
||||
name: Release Packages
|
||||
|
||||
# Triggered after the main release.yml publishes a release.
|
||||
# By the time this fires, macOS (aarch64 + x86_64) and Linux x86_64
|
||||
# CLI tarballs are already attached to the release by release.yml.
|
||||
#
|
||||
# This workflow adds:
|
||||
# 1. Linux arm64 CLI tarball
|
||||
# 2. Homebrew tap formula update
|
||||
# 3. Debian apt repository (gh-pages)
|
||||
# 4. npm package publish
|
||||
# 5. Smoke tests for each channel
|
||||
# 6. One-time backlog issue for future package managers
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: write
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: release-packages-${{ github.event.release.tag_name }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 1. Build Linux arm64 CLI tarball (native runner)
|
||||
# Requires: ubuntu-24.04-arm GitHub-hosted runner (free for public repos).
|
||||
# If this runner type is unavailable on your plan, replace runs-on with
|
||||
# ubuntu-22.04 and add: uses: taiki-e/install-action@cross + use
|
||||
# `cross build --target aarch64-unknown-linux-gnu` instead of plain cargo.
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
build-cli-linux-arm64:
|
||||
name: Build Linux arm64 CLI tarball
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- name: Checkout tag
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
fetch-depth: 1
|
||||
submodules: true
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@1.93.0
|
||||
|
||||
- name: Cache Cargo
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
key: linux-arm64-release
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
pkg-config libssl-dev build-essential
|
||||
|
||||
- name: Build CLI binary
|
||||
run: cargo build --release --bin openhuman-core
|
||||
env:
|
||||
OPENHUMAN_SENTRY_DSN: ${{ vars.OPENHUMAN_SENTRY_DSN }}
|
||||
|
||||
- name: Package tarball and upload to release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
TARGET="aarch64-unknown-linux-gnu"
|
||||
TARBALL="openhuman-core-${VERSION}-${TARGET}.tar.gz"
|
||||
|
||||
WORK=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
cp target/release/openhuman-core "$WORK/"
|
||||
chmod +x "$WORK/openhuman-core"
|
||||
tar -czf "$TARBALL" -C "$WORK" openhuman-core
|
||||
openssl dgst -sha256 -r "$TARBALL" | awk '{print $1}' > "${TARBALL}.sha256"
|
||||
|
||||
gh release upload "${{ github.event.release.tag_name }}" \
|
||||
"$TARBALL" "${TARBALL}.sha256" \
|
||||
--repo tinyhumansai/openhuman --clobber
|
||||
|
||||
echo "[pkg] Uploaded $TARBALL"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 2. Update Homebrew tap
|
||||
# Requires secret: HOMEBREW_TAP_TOKEN (PAT or App token with contents:write
|
||||
# on tinyhumansai/homebrew-openhuman)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
update-homebrew:
|
||||
name: Update Homebrew tap formula
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-cli-linux-arm64]
|
||||
steps:
|
||||
- name: Checkout main repo (for formula template)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
path: src
|
||||
|
||||
- name: Checkout Homebrew tap
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: tinyhumansai/homebrew-openhuman
|
||||
token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
||||
path: tap
|
||||
|
||||
- name: Download tarballs and compute checksums
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${{ github.event.release.tag_name }}"
|
||||
VERSION="${TAG#v}"
|
||||
mkdir -p /tmp/tarballs
|
||||
|
||||
for row in \
|
||||
"aarch64-apple-darwin:SHA256_MACOS_ARM64" \
|
||||
"x86_64-apple-darwin:SHA256_MACOS_X64" \
|
||||
"x86_64-unknown-linux-gnu:SHA256_LINUX_X64" \
|
||||
"aarch64-unknown-linux-gnu:SHA256_LINUX_ARM64"
|
||||
do
|
||||
TARGET="${row%%:*}"
|
||||
VAR="${row##*:}"
|
||||
TARBALL="openhuman-core-${VERSION}-${TARGET}.tar.gz"
|
||||
echo "[homebrew] Downloading $TARBALL ..."
|
||||
gh release download "$TAG" \
|
||||
--pattern "$TARBALL" \
|
||||
--repo tinyhumansai/openhuman \
|
||||
--dir /tmp/tarballs
|
||||
SHA=$(openssl dgst -sha256 -r "/tmp/tarballs/$TARBALL" | awk '{print $1}')
|
||||
echo "${VAR}=${SHA}" >> "$GITHUB_ENV"
|
||||
echo "[homebrew] $TARGET → $SHA"
|
||||
done
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Render and commit formula
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p tap/Formula
|
||||
|
||||
sed \
|
||||
-e "s/@VERSION@/${VERSION}/g" \
|
||||
-e "s/@SHA256_MACOS_ARM64@/${SHA256_MACOS_ARM64}/g" \
|
||||
-e "s/@SHA256_MACOS_X64@/${SHA256_MACOS_X64}/g" \
|
||||
-e "s/@SHA256_LINUX_X64@/${SHA256_LINUX_X64}/g" \
|
||||
-e "s/@SHA256_LINUX_ARM64@/${SHA256_LINUX_ARM64}/g" \
|
||||
src/packages/homebrew/openhuman.rb > tap/Formula/openhuman.rb
|
||||
|
||||
cd tap
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add Formula/openhuman.rb
|
||||
git diff --cached --quiet && echo "[homebrew] No changes to commit." && exit 0
|
||||
git commit -m "chore: update formula to v${VERSION}"
|
||||
git push
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 3. Build Debian apt repository and deploy to GitHub Pages
|
||||
# Requires: APT_SIGNING_KEY (ASCII-armor GPG private key secret)
|
||||
# APT_SIGNING_KEY_ID (key fingerprint / ID)
|
||||
# GitHub Pages must be enabled (Settings → Pages → Source: gh-pages branch)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
build-apt-repo:
|
||||
name: Build apt repository
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-cli-linux-arm64]
|
||||
steps:
|
||||
- name: Checkout tag
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install apt-repo build tools
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y --no-install-recommends \
|
||||
dpkg-dev apt-utils gnupg2
|
||||
|
||||
- name: Import GPG signing key
|
||||
env:
|
||||
APT_SIGNING_KEY: ${{ secrets.APT_SIGNING_KEY }}
|
||||
run: |
|
||||
echo "$APT_SIGNING_KEY" | gpg --batch --import
|
||||
gpg --list-secret-keys
|
||||
|
||||
- name: Download Linux CLI tarballs from release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
TAG="${{ github.event.release.tag_name }}"
|
||||
VERSION="${TAG#v}"
|
||||
mkdir -p /tmp/tarballs
|
||||
|
||||
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
|
||||
TARBALL="openhuman-core-${VERSION}-${target}.tar.gz"
|
||||
gh release download "$TAG" \
|
||||
--pattern "$TARBALL" \
|
||||
--repo tinyhumansai/openhuman \
|
||||
--dir /tmp/tarballs
|
||||
echo "[apt] Downloaded $TARBALL"
|
||||
done
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Extract binaries and build .deb packages
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p /tmp/bins
|
||||
|
||||
tar -xzf "/tmp/tarballs/openhuman-core-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" \
|
||||
-C /tmp/bins
|
||||
mv /tmp/bins/openhuman-core /tmp/bins/openhuman-core-amd64
|
||||
|
||||
tar -xzf "/tmp/tarballs/openhuman-core-${VERSION}-aarch64-unknown-linux-gnu.tar.gz" \
|
||||
-C /tmp/bins
|
||||
mv /tmp/bins/openhuman-core /tmp/bins/openhuman-core-arm64
|
||||
|
||||
chmod +x /tmp/bins/openhuman-core-amd64 /tmp/bins/openhuman-core-arm64
|
||||
|
||||
bash packages/deb/build.sh /tmp/bins/openhuman-core-amd64 "${VERSION}" amd64
|
||||
bash packages/deb/build.sh /tmp/bins/openhuman-core-arm64 "${VERSION}" arm64
|
||||
|
||||
ls -lh openhuman_*.deb
|
||||
|
||||
- name: Build apt repository
|
||||
env:
|
||||
APT_SIGNING_KEY_ID: ${{ secrets.APT_SIGNING_KEY_ID }}
|
||||
run: |
|
||||
bash scripts/build-apt-repo.sh /tmp/apt-repo \
|
||||
"openhuman_${VERSION}_amd64.deb" \
|
||||
"openhuman_${VERSION}_arm64.deb"
|
||||
|
||||
- name: Checkout gh-pages branch
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: gh-pages
|
||||
path: gh-pages
|
||||
# Create the branch if it doesn't exist
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Update apt/ directory on gh-pages
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p gh-pages/apt
|
||||
# Remove old apt content, replace with freshly built repo
|
||||
rm -rf gh-pages/apt/*
|
||||
cp -r /tmp/apt-repo/. gh-pages/apt/
|
||||
|
||||
cd gh-pages
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add apt/
|
||||
git diff --cached --quiet && echo "[apt] No changes." && exit 0
|
||||
git commit -m "chore(apt): publish v${VERSION}"
|
||||
git push origin gh-pages
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 4. Publish npm package
|
||||
# Requires secret: NPM_TOKEN (automation token from npmjs.com)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
publish-npm:
|
||||
name: Publish npm package
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout tag
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.x
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Set version and publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
|
||||
cd packages/npm
|
||||
|
||||
# Stamp version without creating a git commit
|
||||
npm version "$VERSION" --no-git-tag-version
|
||||
|
||||
# SKIP_OPENHUMAN_BINARY_DOWNLOAD prevents postinstall from running
|
||||
# during publish (the binary doesn't exist yet on the publish runner)
|
||||
SKIP_OPENHUMAN_BINARY_DOWNLOAD=1 npm publish --access public
|
||||
|
||||
echo "[npm] Published openhuman@${VERSION}"
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 5. Smoke test: Homebrew
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
smoke-homebrew:
|
||||
name: Smoke — Homebrew (${{ matrix.os }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [update-homebrew]
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-22.04]
|
||||
steps:
|
||||
- name: Install Homebrew (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
echo "/home/linuxbrew/.linuxbrew/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Tap and install
|
||||
run: |
|
||||
brew tap tinyhumansai/openhuman
|
||||
brew install openhuman
|
||||
|
||||
- name: Smoke test
|
||||
run: openhuman --version
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 6. Smoke test: apt
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
smoke-apt:
|
||||
name: Smoke — apt (ubuntu-22.04)
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-apt-repo]
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Add apt repository
|
||||
run: |
|
||||
sudo apt-get install -y --no-install-recommends gnupg2 curl ca-certificates
|
||||
curl -fsSL https://tinyhumansai.github.io/openhuman/apt/KEY.gpg \
|
||||
| sudo gpg --dearmor -o /etc/apt/keyrings/openhuman.gpg
|
||||
echo "deb [signed-by=/etc/apt/keyrings/openhuman.gpg arch=amd64] \
|
||||
https://tinyhumansai.github.io/openhuman/apt stable main" \
|
||||
| sudo tee /etc/apt/sources.list.d/openhuman.list
|
||||
|
||||
- name: Install and smoke test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y openhuman
|
||||
openhuman --version
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 7. Smoke test: npm
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
smoke-npm:
|
||||
name: Smoke — npm (${{ matrix.os }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [publish-npm]
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.x
|
||||
|
||||
- name: Wait for npm propagation, then install
|
||||
run: |
|
||||
VERSION="${{ github.event.release.tag_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
# npm can take up to ~2 min to propagate a new publish
|
||||
for i in 1 2 3 4 5; do
|
||||
npm install -g "openhuman@${VERSION}" && break || sleep 30
|
||||
done
|
||||
|
||||
- name: Smoke test
|
||||
run: openhuman --version
|
||||
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# 8. File the "future package managers" backlog issue (once ever)
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
create-backlog-issue:
|
||||
name: Create backlog issue (once)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create issue if it doesn't exist
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const label = 'distribution-backlog';
|
||||
const title = '[Backlog] Package manager distribution — next tiers';
|
||||
|
||||
// Check for existing open or closed issue with this exact title
|
||||
const { data: existing } = await github.rest.issues.listForRepo({
|
||||
owner, repo,
|
||||
state: 'all',
|
||||
labels: label,
|
||||
per_page: 10,
|
||||
});
|
||||
if (existing.some(i => i.title === title)) {
|
||||
core.info('Backlog issue already exists — skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the label exists
|
||||
try {
|
||||
await github.rest.issues.createLabel({
|
||||
owner, repo,
|
||||
name: label,
|
||||
color: '0075ca',
|
||||
description: 'Package distribution backlog',
|
||||
});
|
||||
} catch (_) { /* label may already exist */ }
|
||||
|
||||
const body = `## Summary
|
||||
|
||||
Track remaining package manager channels. Each tier reflects expected maintenance commitment from the core team.
|
||||
|
||||
## Tier 1 — Official (core team maintains)
|
||||
|
||||
- [ ] **npx / pnpm dlx** — zero-install via the npm package already published; document the one-liner: \`npx openhuman@latest\`
|
||||
- [ ] **Scoop (Windows)** — needs a Windows binary (un-comment the Windows matrix in \`release.yml\` first); add a \`tinyhumansai/scoop-openhuman\` bucket
|
||||
|
||||
## Tier 2 — Community-supported (PRs welcome, core team reviews)
|
||||
|
||||
- [ ] **AUR (Arch Linux)** — add \`PKGBUILD\` pointing at the GitHub release tarball; list in \`packages/\`
|
||||
- [ ] **Nix / nixpkgs** — upstream a \`pkgs/tools/openhuman/default.nix\` derivation; document local flake overlay as interim
|
||||
|
||||
## Tier 3 — Planned (no timeline)
|
||||
|
||||
- [ ] **Snap / Snapcraft** — \`snapcraft.yaml\`, publish to Snap Store
|
||||
- [ ] **Flatpak** — \`org.tinyhumans.Openhuman.yaml\`, publish to Flathub
|
||||
- [ ] **WinGet** — manifest in \`microsoft/winget-pkgs\` once Windows binary is stable
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Each official channel has a CI smoke test (install + \`openhuman --version\`)
|
||||
- [ ] Install commands appear in \`docs/install.md\`
|
||||
- [ ] Checksums shipped for all artifacts
|
||||
`;
|
||||
const issue = await github.rest.issues.create({
|
||||
owner, repo,
|
||||
title,
|
||||
body,
|
||||
labels: [label],
|
||||
});
|
||||
core.info(\`Created backlog issue: \${issue.data.html_url}\`);
|
||||
@@ -678,6 +678,48 @@ jobs:
|
||||
echo "Checking staple..."
|
||||
xcrun stapler validate "$APP_PATH" || echo "WARNING: Staple validation failed"
|
||||
|
||||
- name: Package CLI tarball and upload to release
|
||||
if: matrix.settings.platform != 'windows-latest'
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ needs.prepare-release.outputs.version }}
|
||||
MATRIX_PLATFORM: ${{ matrix.settings.platform }}
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
TARBALL="openhuman-core-${VERSION}-${MATRIX_TARGET}.tar.gz"
|
||||
WORK=$(mktemp -d)
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
|
||||
if [[ "$MATRIX_PLATFORM" == "macos-latest" ]]; then
|
||||
# Prefer the notarized+signed binary extracted from inside the .app bundle
|
||||
APP_PATH="${{ steps.locate-app.outputs.app_path }}"
|
||||
BIN=$(find "$APP_PATH/Contents/MacOS" -maxdepth 1 -name "openhuman-core-*" \
|
||||
! -name "*.sig" 2>/dev/null | head -1 || true)
|
||||
[[ -z "$BIN" ]] && BIN=$(find "$APP_PATH/Contents/Resources" -maxdepth 1 \
|
||||
-name "openhuman-core-*" ! -name "*.sig" 2>/dev/null | head -1 || true)
|
||||
if [[ -z "$BIN" ]]; then
|
||||
echo "[pkg] Falling back to target dir binary (no notarized sidecar found)"
|
||||
BIN="${{ steps.cli-paths.outputs.cli_path }}"
|
||||
fi
|
||||
else
|
||||
BIN="${{ steps.cli-paths.outputs.cli_path }}"
|
||||
fi
|
||||
|
||||
cp "$BIN" "$WORK/openhuman-core"
|
||||
chmod +x "$WORK/openhuman-core"
|
||||
tar -czf "$TARBALL" -C "$WORK" openhuman-core
|
||||
|
||||
# openssl dgst works on both macOS and Linux runners
|
||||
openssl dgst -sha256 -r "$TARBALL" | awk '{print $1}' > "${TARBALL}.sha256"
|
||||
|
||||
gh release upload "v${VERSION}" "$TARBALL" "${TARBALL}.sha256" \
|
||||
--repo tinyhumansai/openhuman --clobber
|
||||
|
||||
echo "[pkg] Uploaded $TARBALL and ${TARBALL}.sha256"
|
||||
|
||||
- name: Upload standalone CLI artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
# Installing OpenHuman
|
||||
|
||||
## Quick install
|
||||
|
||||
| Package manager | Command | OS |
|
||||
|---|---|---|
|
||||
| **Homebrew** | `brew install tinyhumansai/openhuman/openhuman` | macOS, Linux |
|
||||
| **apt** | `sudo apt install openhuman` (see [setup](#apt-debianubuntu)) | Debian, Ubuntu |
|
||||
| **npm** | `npm install -g openhuman` | Any (Node ≥ 18) |
|
||||
| **curl** | `curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh \| bash` | macOS, Linux |
|
||||
|
||||
---
|
||||
|
||||
## Homebrew (macOS / Linux)
|
||||
|
||||
```bash
|
||||
brew install tinyhumansai/openhuman/openhuman
|
||||
```
|
||||
|
||||
**Update:**
|
||||
```bash
|
||||
brew upgrade openhuman
|
||||
```
|
||||
|
||||
**Uninstall:**
|
||||
```bash
|
||||
brew uninstall openhuman
|
||||
brew untap tinyhumansai/openhuman # optional: remove tap
|
||||
```
|
||||
|
||||
Homebrew installs the binary as `openhuman`. The tap lives at
|
||||
[tinyhumansai/homebrew-openhuman](https://github.com/tinyhumansai/homebrew-openhuman).
|
||||
|
||||
---
|
||||
|
||||
## apt (Debian / Ubuntu)
|
||||
|
||||
### 1. Add the repository key and source
|
||||
|
||||
```bash
|
||||
sudo apt-get install -y gnupg2 curl ca-certificates
|
||||
|
||||
curl -fsSL https://tinyhumansai.github.io/openhuman/apt/KEY.gpg \
|
||||
| sudo gpg --dearmor -o /etc/apt/keyrings/openhuman.gpg
|
||||
|
||||
echo "deb [signed-by=/etc/apt/keyrings/openhuman.gpg arch=amd64] \
|
||||
https://tinyhumansai.github.io/openhuman/apt stable main" \
|
||||
| sudo tee /etc/apt/sources.list.d/openhuman.list
|
||||
```
|
||||
|
||||
> **arm64:** replace `arch=amd64` with `arch=arm64` or `arch=amd64,arm64`.
|
||||
|
||||
### 2. Install
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install openhuman
|
||||
```
|
||||
|
||||
**Update:**
|
||||
```bash
|
||||
sudo apt-get update && sudo apt-get upgrade openhuman
|
||||
```
|
||||
|
||||
**Uninstall:**
|
||||
```bash
|
||||
sudo apt-get remove openhuman
|
||||
# remove repository (optional):
|
||||
sudo rm /etc/apt/sources.list.d/openhuman.list /etc/apt/keyrings/openhuman.gpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## npm
|
||||
|
||||
```bash
|
||||
npm install -g openhuman
|
||||
```
|
||||
|
||||
**Update:**
|
||||
```bash
|
||||
npm update -g openhuman
|
||||
```
|
||||
|
||||
**Uninstall:**
|
||||
```bash
|
||||
npm uninstall -g openhuman
|
||||
```
|
||||
|
||||
The npm package is a thin wrapper that downloads the platform-native binary on
|
||||
first install and verifies its SHA-256 checksum before placing it. Node.js ≥ 18
|
||||
is required; the binary itself has no Node dependency at runtime.
|
||||
|
||||
---
|
||||
|
||||
## curl / manual install
|
||||
|
||||
```bash
|
||||
curl -fsSL \
|
||||
https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh \
|
||||
| bash
|
||||
```
|
||||
|
||||
Pass `--dry-run` to preview actions without installing:
|
||||
|
||||
```bash
|
||||
bash scripts/install.sh --dry-run --verbose
|
||||
```
|
||||
|
||||
**Uninstall (manual):**
|
||||
```bash
|
||||
rm "$(which openhuman)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Support policy
|
||||
|
||||
| Channel | Tier | Maintained by |
|
||||
|---|---|---|
|
||||
| Homebrew | **Official** | Core team |
|
||||
| apt | **Official** | Core team |
|
||||
| npm | **Official** | Core team |
|
||||
| curl / install.sh | **Official** | Core team |
|
||||
| AUR (Arch) | Community | Community PRs |
|
||||
| Nix | Community | Community PRs |
|
||||
| Scoop (Windows) | Planned | — |
|
||||
| Snap / Flatpak | Planned | — |
|
||||
|
||||
See [tinyhumansai/openhuman#distribution-backlog](https://github.com/tinyhumansai/openhuman/issues?q=label%3Adistribution-backlog) for the next channels in the pipeline.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### macOS Gatekeeper warning
|
||||
|
||||
If macOS blocks the binary with *"cannot be opened because the developer cannot be verified"*:
|
||||
|
||||
```bash
|
||||
# Option 1: approve via System Settings → Privacy & Security → Allow Anyway
|
||||
# Option 2: remove quarantine flag (Homebrew install should handle this automatically)
|
||||
xattr -d com.apple.quarantine "$(which openhuman)"
|
||||
```
|
||||
|
||||
Binaries installed via Homebrew or the signed `.app` bundle are notarized by
|
||||
Apple and should not trigger Gatekeeper.
|
||||
|
||||
### apt: "NO_PUBKEY" error
|
||||
|
||||
Re-import the key:
|
||||
```bash
|
||||
curl -fsSL https://tinyhumansai.github.io/openhuman/apt/KEY.gpg \
|
||||
| sudo gpg --dearmor -o /etc/apt/keyrings/openhuman.gpg
|
||||
sudo apt-get update
|
||||
```
|
||||
|
||||
### npm: binary not found after install
|
||||
|
||||
The postinstall script may have failed silently. Re-run it manually:
|
||||
```bash
|
||||
FORCE_REINSTALL=1 node "$(npm root -g)/openhuman/install.js"
|
||||
```
|
||||
|
||||
Or reinstall cleanly:
|
||||
```bash
|
||||
npm uninstall -g openhuman && npm install -g openhuman
|
||||
```
|
||||
|
||||
### Verify checksum manually
|
||||
|
||||
Every release asset ships a companion `.sha256` file:
|
||||
|
||||
```bash
|
||||
VERSION=0.49.33
|
||||
TARGET=x86_64-unknown-linux-gnu
|
||||
curl -fsSLO "https://github.com/tinyhumansai/openhuman/releases/download/v${VERSION}/openhuman-core-${VERSION}-${TARGET}.tar.gz"
|
||||
curl -fsSLO "https://github.com/tinyhumansai/openhuman/releases/download/v${VERSION}/openhuman-core-${VERSION}-${TARGET}.tar.gz.sha256"
|
||||
echo "$(cat openhuman-core-${VERSION}-${TARGET}.tar.gz.sha256) openhuman-core-${VERSION}-${TARGET}.tar.gz" | sha256sum --check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Release artifacts reference
|
||||
|
||||
Each release attaches the following files:
|
||||
|
||||
| Artifact | Platform |
|
||||
|---|---|
|
||||
| `openhuman-core-<v>-aarch64-apple-darwin.tar.gz` | macOS Apple Silicon |
|
||||
| `openhuman-core-<v>-x86_64-apple-darwin.tar.gz` | macOS Intel |
|
||||
| `openhuman-core-<v>-x86_64-unknown-linux-gnu.tar.gz` | Linux x86-64 |
|
||||
| `openhuman-core-<v>-aarch64-unknown-linux-gnu.tar.gz` | Linux arm64 |
|
||||
| `OpenHuman_<v>_aarch64.dmg` | macOS desktop app (Apple Silicon) |
|
||||
| `OpenHuman_<v>_x64.dmg` | macOS desktop app (Intel) |
|
||||
| `OpenHuman_<v>_amd64.deb` | Linux desktop app (.deb) |
|
||||
| `OpenHuman_<v>_amd64.AppImage` | Linux desktop app (AppImage) |
|
||||
|
||||
Every archive has a corresponding `.sha256` companion file.
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build a .deb package for the openhuman-core CLI binary.
|
||||
# Usage: build.sh <binary_path> <version> <arch>
|
||||
# arch: amd64 | arm64
|
||||
set -euo pipefail
|
||||
|
||||
BINARY="$1"
|
||||
VERSION="$2"
|
||||
ARCH="$3"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||
|
||||
PKG_NAME="openhuman_${VERSION}_${ARCH}"
|
||||
PKG_DIR="$WORK_DIR/$PKG_NAME"
|
||||
|
||||
mkdir -p "$PKG_DIR/usr/bin"
|
||||
mkdir -p "$PKG_DIR/DEBIAN"
|
||||
|
||||
install -m 755 "$BINARY" "$PKG_DIR/usr/bin/openhuman"
|
||||
|
||||
sed \
|
||||
-e "s/@VERSION@/${VERSION}/g" \
|
||||
-e "s/@ARCH@/${ARCH}/g" \
|
||||
"$SCRIPT_DIR/control.in" > "$PKG_DIR/DEBIAN/control"
|
||||
|
||||
OUTPUT="${PKG_NAME}.deb"
|
||||
dpkg-deb --build --root-owner-group "$PKG_DIR" "$OUTPUT"
|
||||
echo "[deb] Built: $OUTPUT"
|
||||
@@ -0,0 +1,10 @@
|
||||
Package: openhuman
|
||||
Version: @VERSION@
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: @ARCH@
|
||||
Maintainer: OpenHuman <hello@tinyhumans.ai>
|
||||
Homepage: https://github.com/tinyhumansai/openhuman
|
||||
Description: AI-powered assistant for communities
|
||||
OpenHuman is an AI-powered CLI for crypto communities and
|
||||
collaborative workspaces.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Homebrew formula template — rendered by CI, committed to tinyhumansai/homebrew-openhuman.
|
||||
# Placeholders replaced by .github/workflows/release-packages.yml before commit.
|
||||
class Openhuman < Formula
|
||||
desc "AI-powered assistant for communities — OpenHuman CLI"
|
||||
homepage "https://github.com/tinyhumansai/openhuman"
|
||||
version "@VERSION@"
|
||||
license "MIT"
|
||||
|
||||
on_macos do
|
||||
on_arm do
|
||||
url "https://github.com/tinyhumansai/openhuman/releases/download/v@VERSION@/openhuman-core-@VERSION@-aarch64-apple-darwin.tar.gz"
|
||||
sha256 "@SHA256_MACOS_ARM64@"
|
||||
end
|
||||
on_intel do
|
||||
url "https://github.com/tinyhumansai/openhuman/releases/download/v@VERSION@/openhuman-core-@VERSION@-x86_64-apple-darwin.tar.gz"
|
||||
sha256 "@SHA256_MACOS_X64@"
|
||||
end
|
||||
end
|
||||
|
||||
on_linux do
|
||||
on_arm do
|
||||
# ARM64 (aarch64)
|
||||
url "https://github.com/tinyhumansai/openhuman/releases/download/v@VERSION@/openhuman-core-@VERSION@-aarch64-unknown-linux-gnu.tar.gz"
|
||||
sha256 "@SHA256_LINUX_ARM64@"
|
||||
end
|
||||
on_intel do
|
||||
url "https://github.com/tinyhumansai/openhuman/releases/download/v@VERSION@/openhuman-core-@VERSION@-x86_64-unknown-linux-gnu.tar.gz"
|
||||
sha256 "@SHA256_LINUX_X64@"
|
||||
end
|
||||
end
|
||||
|
||||
def install
|
||||
bin.install "openhuman-core" => "openhuman"
|
||||
end
|
||||
|
||||
test do
|
||||
system "#{bin}/openhuman", "--version"
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
# Exclude the downloaded native binary from published package
|
||||
bin/openhuman-bin
|
||||
bin/openhuman-bin.exe
|
||||
bin/*.tar.gz
|
||||
bin/*.zip
|
||||
bin/*.sha256
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
const { spawnSync } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const isWin = process.platform === 'win32';
|
||||
const binName = isWin ? 'openhuman-bin.exe' : 'openhuman-bin';
|
||||
const binPath = path.join(__dirname, binName);
|
||||
|
||||
const result = spawnSync(binPath, process.argv.slice(2), {
|
||||
stdio: 'inherit',
|
||||
windowsHide: false,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
if (result.error.code === 'ENOENT') {
|
||||
process.stderr.write(
|
||||
'openhuman binary not found. Try reinstalling: npm install -g openhuman\n'
|
||||
);
|
||||
} else {
|
||||
process.stderr.write(`openhuman: ${result.error.message}\n`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
process.exit(result.status ?? 0);
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
// postinstall: downloads the correct pre-built binary for this platform/arch,
|
||||
// verifies the SHA-256 checksum, then places it at bin/openhuman-bin[.exe].
|
||||
//
|
||||
// The binary is fetched from the GitHub release that matches package.json version.
|
||||
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const REPO = 'tinyhumansai/openhuman';
|
||||
const pkg = require('./package.json');
|
||||
const VERSION = pkg.version;
|
||||
|
||||
// Maps process.platform + process.arch → Rust target triple
|
||||
const TARGET_MAP = {
|
||||
darwin: { x64: 'x86_64-apple-darwin', arm64: 'aarch64-apple-darwin' },
|
||||
linux: { x64: 'x86_64-unknown-linux-gnu', arm64: 'aarch64-unknown-linux-gnu' },
|
||||
win32: { x64: 'x86_64-pc-windows-msvc' },
|
||||
};
|
||||
|
||||
function getTarget() {
|
||||
const platform = process.platform;
|
||||
const arch = process.arch;
|
||||
const targets = TARGET_MAP[platform];
|
||||
if (!targets) throw new Error(`Unsupported platform: ${platform}`);
|
||||
const target = targets[arch];
|
||||
if (!target) throw new Error(`Unsupported arch ${arch} on ${platform}`);
|
||||
return { platform, target };
|
||||
}
|
||||
|
||||
function httpsGet(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function request(u) {
|
||||
https.get(u, (res) => {
|
||||
if (res.statusCode === 301 || res.statusCode === 302) {
|
||||
return request(res.headers.location);
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
res.resume();
|
||||
return reject(new Error(`HTTP ${res.statusCode} fetching ${u}`));
|
||||
}
|
||||
const chunks = [];
|
||||
res.on('data', (c) => chunks.push(c));
|
||||
res.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
res.on('error', reject);
|
||||
}).on('error', reject);
|
||||
}
|
||||
request(url);
|
||||
});
|
||||
}
|
||||
|
||||
function downloadFile(url, dest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function request(u) {
|
||||
https.get(u, (res) => {
|
||||
if (res.statusCode === 301 || res.statusCode === 302) {
|
||||
return request(res.headers.location);
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
res.resume();
|
||||
return reject(new Error(`HTTP ${res.statusCode} fetching ${u}`));
|
||||
}
|
||||
const out = fs.createWriteStream(dest);
|
||||
res.pipe(out);
|
||||
out.on('finish', () => out.close(resolve));
|
||||
out.on('error', reject);
|
||||
res.on('error', reject);
|
||||
}).on('error', reject);
|
||||
}
|
||||
request(url);
|
||||
});
|
||||
}
|
||||
|
||||
function sha256hex(filePath) {
|
||||
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Skip in CI environments that just need the package metadata
|
||||
if (process.env.SKIP_OPENHUMAN_BINARY_DOWNLOAD) {
|
||||
console.log('[openhuman] Skipping binary download (SKIP_OPENHUMAN_BINARY_DOWNLOAD set)');
|
||||
return;
|
||||
}
|
||||
|
||||
const { platform, target } = getTarget();
|
||||
const isWin = platform === 'win32';
|
||||
const ext = isWin ? '.zip' : '.tar.gz';
|
||||
const tarball = `openhuman-core-${VERSION}-${target}${ext}`;
|
||||
const checksumFile = `${tarball}.sha256`;
|
||||
const baseUrl = `https://github.com/${REPO}/releases/download/v${VERSION}`;
|
||||
|
||||
const binDir = path.join(__dirname, 'bin');
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
|
||||
const tmpTarball = path.join(binDir, tarball);
|
||||
const binDest = path.join(binDir, isWin ? 'openhuman-bin.exe' : 'openhuman-bin');
|
||||
|
||||
// Skip if binary already exists and is executable
|
||||
if (fs.existsSync(binDest)) {
|
||||
console.log('[openhuman] Binary already installed, skipping download.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[openhuman] Downloading v${VERSION} for ${target}...`);
|
||||
|
||||
// Download checksum first (small)
|
||||
const checksumData = await httpsGet(`${baseUrl}/${checksumFile}`);
|
||||
const expectedChecksum = checksumData.toString('utf8').trim().split(/\s+/)[0];
|
||||
|
||||
// Download binary archive
|
||||
await downloadFile(`${baseUrl}/${tarball}`, tmpTarball);
|
||||
|
||||
// Verify checksum
|
||||
const actualChecksum = sha256hex(tmpTarball);
|
||||
if (expectedChecksum !== actualChecksum) {
|
||||
fs.rmSync(tmpTarball, { force: true });
|
||||
throw new Error(
|
||||
`[openhuman] Checksum mismatch!\n expected: ${expectedChecksum}\n got: ${actualChecksum}`
|
||||
);
|
||||
}
|
||||
console.log('[openhuman] Checksum verified.');
|
||||
|
||||
// Extract
|
||||
if (isWin) {
|
||||
// PowerShell is available on Windows runners
|
||||
execSync(
|
||||
`powershell -Command "Expand-Archive -Path '${tmpTarball}' -DestinationPath '${binDir}' -Force"`,
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
const extracted = path.join(binDir, 'openhuman-core.exe');
|
||||
if (fs.existsSync(extracted)) fs.renameSync(extracted, binDest);
|
||||
} else {
|
||||
execSync(`tar -xzf "${tmpTarball}" -C "${binDir}"`, { stdio: 'inherit' });
|
||||
const extracted = path.join(binDir, 'openhuman-core');
|
||||
if (fs.existsSync(extracted)) {
|
||||
fs.renameSync(extracted, binDest);
|
||||
fs.chmodSync(binDest, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up archive
|
||||
fs.rmSync(tmpTarball, { force: true });
|
||||
|
||||
if (!fs.existsSync(binDest)) {
|
||||
throw new Error('[openhuman] Extraction failed — binary not found after unpack.');
|
||||
}
|
||||
|
||||
console.log(`[openhuman] Installed at ${binDest}`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error('\n[openhuman] Installation failed:', err.message);
|
||||
console.error('You can file a bug at https://github.com/tinyhumansai/openhuman/issues');
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "openhuman",
|
||||
"version": "0.0.0",
|
||||
"description": "AI-powered assistant for communities — OpenHuman CLI",
|
||||
"keywords": [
|
||||
"openhuman",
|
||||
"ai",
|
||||
"cli",
|
||||
"crypto",
|
||||
"community"
|
||||
],
|
||||
"homepage": "https://github.com/tinyhumansai/openhuman",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tinyhumansai/openhuman.git",
|
||||
"directory": "packages/npm"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/tinyhumansai/openhuman/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"openhuman": "./bin/openhuman.js"
|
||||
},
|
||||
"scripts": {
|
||||
"postinstall": "node install.js"
|
||||
},
|
||||
"files": [
|
||||
"bin/",
|
||||
"install.js",
|
||||
"README.md"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build a signed Debian apt repository from one or more .deb files.
|
||||
# Requires: dpkg-dev (dpkg-scanpackages), apt-utils (apt-ftparchive), gzip, gpg, python3
|
||||
#
|
||||
# Usage:
|
||||
# build-apt-repo.sh <output_dir> <pkg1.deb> [<pkg2.deb> ...]
|
||||
#
|
||||
# The GPG signing key must be imported into the agent before calling.
|
||||
# Set APT_SIGNING_KEY_ID to select the key; leave unset to use the default.
|
||||
set -euo pipefail
|
||||
|
||||
OUTPUT_DIR="$1"; shift
|
||||
DEB_FILES=("$@")
|
||||
|
||||
echo "[apt-repo] Building repository at $OUTPUT_DIR"
|
||||
|
||||
# ── Pool ───────────────────────────────────────────────────────────────────────
|
||||
mkdir -p "$OUTPUT_DIR/pool/main"
|
||||
for deb in "${DEB_FILES[@]}"; do
|
||||
cp "$deb" "$OUTPUT_DIR/pool/main/"
|
||||
echo "[apt-repo] + pool/main/$(basename "$deb")"
|
||||
done
|
||||
|
||||
# ── Per-architecture Packages files ───────────────────────────────────────────
|
||||
FILTER_PY="$(mktemp --suffix=.py)"
|
||||
trap 'rm -f "$FILTER_PY"' EXIT
|
||||
|
||||
cat > "$FILTER_PY" << 'PYEOF'
|
||||
import sys, re
|
||||
|
||||
arch = sys.argv[1]
|
||||
data = open(sys.argv[2]).read()
|
||||
out = []
|
||||
for block in data.strip().split('\n\n'):
|
||||
if re.search(r'^Architecture:\s+' + re.escape(arch) + r'\s*$', block, re.MULTILINE):
|
||||
out.append(block.rstrip())
|
||||
if out:
|
||||
print('\n\n'.join(out) + '\n')
|
||||
PYEOF
|
||||
|
||||
ALL_PACKAGES="$(mktemp)"
|
||||
(cd "$OUTPUT_DIR" && dpkg-scanpackages --multiversion pool/main 2>/dev/null) > "$ALL_PACKAGES"
|
||||
|
||||
for arch in amd64 arm64; do
|
||||
dir="$OUTPUT_DIR/dists/stable/main/binary-${arch}"
|
||||
mkdir -p "$dir"
|
||||
python3 "$FILTER_PY" "$arch" "$ALL_PACKAGES" > "$dir/Packages"
|
||||
gzip -9c "$dir/Packages" > "$dir/Packages.gz"
|
||||
lines=$(wc -l < "$dir/Packages")
|
||||
echo "[apt-repo] binary-${arch}/Packages: ${lines} lines"
|
||||
done
|
||||
rm -f "$ALL_PACKAGES"
|
||||
|
||||
# ── Release file ───────────────────────────────────────────────────────────────
|
||||
RELEASE_CONF="$(mktemp)"
|
||||
cat > "$RELEASE_CONF" << 'EOF'
|
||||
APT::FTPArchive::Release::Origin "OpenHuman";
|
||||
APT::FTPArchive::Release::Label "OpenHuman";
|
||||
APT::FTPArchive::Release::Suite "stable";
|
||||
APT::FTPArchive::Release::Codename "stable";
|
||||
APT::FTPArchive::Release::Architectures "amd64 arm64";
|
||||
APT::FTPArchive::Release::Components "main";
|
||||
APT::FTPArchive::Release::Description "OpenHuman official apt repository";
|
||||
EOF
|
||||
|
||||
(cd "$OUTPUT_DIR" && apt-ftparchive -c "$RELEASE_CONF" release dists/stable) \
|
||||
> "$OUTPUT_DIR/dists/stable/Release"
|
||||
rm -f "$RELEASE_CONF"
|
||||
echo "[apt-repo] Release generated"
|
||||
|
||||
# ── Sign ───────────────────────────────────────────────────────────────────────
|
||||
GPG_ARGS=(--batch --yes)
|
||||
[[ -n "${APT_SIGNING_KEY_ID:-}" ]] && GPG_ARGS+=(--local-user "$APT_SIGNING_KEY_ID")
|
||||
|
||||
gpg "${GPG_ARGS[@]}" --clearsign \
|
||||
-o "$OUTPUT_DIR/dists/stable/InRelease" \
|
||||
"$OUTPUT_DIR/dists/stable/Release"
|
||||
|
||||
gpg "${GPG_ARGS[@]}" -abs \
|
||||
-o "$OUTPUT_DIR/dists/stable/Release.gpg" \
|
||||
"$OUTPUT_DIR/dists/stable/Release"
|
||||
|
||||
echo "[apt-repo] Release signed"
|
||||
|
||||
# ── Export public key ─────────────────────────────────────────────────────────
|
||||
gpg --batch --yes --armor --export ${APT_SIGNING_KEY_ID:-} > "$OUTPUT_DIR/KEY.gpg"
|
||||
echo "[apt-repo] Public key → KEY.gpg"
|
||||
|
||||
echo "[apt-repo] Done. Files:"
|
||||
find "$OUTPUT_DIR" -type f | sort | sed 's|^| |'
|
||||
Reference in New Issue
Block a user