mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
feat(docker): Dockerfile, cloud server support, and parallel release pipeline (#174)
* added doceker build * added docker files * feat(ci): add Docker build phase to release pipeline and .dockerignore Restructure release.yml into parallel build phases: build-desktop (matrix) and build-docker run concurrently after create-release. Docker image is pushed to GHCR and pull instructions are appended to release notes. publish-release now gates on both phases succeeding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply cargo fmt to jsonrpc host binding Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(review): address PR review findings - .env.example: comment out OPENHUMAN_CORE_HOST so Docker's 0.0.0.0 default isn't overridden when users copy the example file - cli.rs: add --host to print_general_help() usage line for consistency - jsonrpc.rs: use tuple bind (host, port) for IPv6 support, add debug logging with source tracking (CLI/env/default) before bind - release.yml: push only staging tag in build-docker, promote to versioned + latest in publish-release after all builds succeed; cleanup-failed-release deletes the staging image on failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
f5b66de2f1
commit
6f8b5d6c9f
@@ -0,0 +1,39 @@
|
||||
# Build artifacts
|
||||
target/
|
||||
app/src-tauri/target/
|
||||
|
||||
# Node / frontend (not needed for core binary)
|
||||
app/
|
||||
node_modules/
|
||||
dist/
|
||||
.vite/
|
||||
|
||||
# IDE / editor
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Git
|
||||
.git/
|
||||
.gitmodules
|
||||
|
||||
# CI / docs
|
||||
.github/
|
||||
docs/
|
||||
*.md
|
||||
!Cargo.lock
|
||||
|
||||
# Environment / secrets
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Tests (not needed in build context)
|
||||
tests/
|
||||
scripts/
|
||||
@@ -23,6 +23,9 @@ JWT_TOKEN=
|
||||
# ---------------------------------------------------------------------------
|
||||
# Core process
|
||||
# ---------------------------------------------------------------------------
|
||||
# [optional] Default: 127.0.0.1 (use 0.0.0.0 for Docker / cloud).
|
||||
# Leave unset to keep the default; the Docker image sets 0.0.0.0 automatically.
|
||||
# OPENHUMAN_CORE_HOST=
|
||||
# [optional] Default: 7788
|
||||
OPENHUMAN_CORE_PORT=7788
|
||||
# [optional] Default: http://127.0.0.1:7788/rpc
|
||||
|
||||
+140
-147
@@ -14,12 +14,38 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
concurrency:
|
||||
group: release-main
|
||||
cancel-in-progress: false
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Job dependency graph
|
||||
#
|
||||
# prepare-release
|
||||
# │
|
||||
# ├─── create-release
|
||||
# │ │
|
||||
# │ ┌────┴────────────┐
|
||||
# │ │ │
|
||||
# │ build-desktop build-docker
|
||||
# │ (matrix: macOS, (GHCR image)
|
||||
# │ linux, windows)
|
||||
# │ │ │
|
||||
# │ └────┬────────────┘
|
||||
# │ │
|
||||
# │ publish-release
|
||||
# │ │
|
||||
# │ notify-discord
|
||||
# │
|
||||
# └─── cleanup-failed-release (on failure)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
jobs:
|
||||
# =========================================================================
|
||||
# Phase 1: Version bump, commit, tag
|
||||
# =========================================================================
|
||||
prepare-release:
|
||||
name: Prepare release commit and tag
|
||||
runs-on: ubuntu-latest
|
||||
@@ -156,6 +182,9 @@ jobs:
|
||||
|
||||
echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# =========================================================================
|
||||
# Phase 2: Create draft GitHub release
|
||||
# =========================================================================
|
||||
create-release:
|
||||
name: Create GitHub release
|
||||
runs-on: ubuntu-latest
|
||||
@@ -200,8 +229,11 @@ jobs:
|
||||
core.setOutput('release_id', String(release.data.id));
|
||||
core.setOutput('upload_url', release.data.upload_url);
|
||||
|
||||
build-artifacts:
|
||||
name: Build and upload artifacts
|
||||
# =========================================================================
|
||||
# Phase 3a: Build desktop artifacts (parallel matrix)
|
||||
# =========================================================================
|
||||
build-desktop:
|
||||
name: "Desktop: ${{ matrix.settings.artifact_suffix }}"
|
||||
needs: [prepare-release, create-release]
|
||||
runs-on: ${{ matrix.settings.platform }}
|
||||
environment: Production
|
||||
@@ -727,12 +759,60 @@ jobs:
|
||||
path: |
|
||||
${{ steps.cli-paths.outputs.cli_path }}
|
||||
|
||||
# =========================================================================
|
||||
# Phase 3b: Build & push Docker image (runs parallel with build-desktop)
|
||||
# =========================================================================
|
||||
build-docker:
|
||||
name: "Docker: build and push"
|
||||
needs: [prepare-release, create-release]
|
||||
runs-on: ubuntu-latest
|
||||
environment: Production
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: tinyhumansai/openhuman-core
|
||||
steps:
|
||||
- name: Checkout tag
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ needs.prepare-release.outputs.tag }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push staging tag
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:staging-${{ needs.prepare-release.outputs.tag }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
labels: |
|
||||
org.opencontainers.image.title=openhuman-core
|
||||
org.opencontainers.image.description=OpenHuman core JSON-RPC server
|
||||
org.opencontainers.image.version=${{ needs.prepare-release.outputs.version }}
|
||||
org.opencontainers.image.source=https://github.com/tinyhumansai/openhuman
|
||||
org.opencontainers.image.revision=${{ needs.prepare-release.outputs.sha }}
|
||||
|
||||
# =========================================================================
|
||||
# Phase 4: Publish the draft release (waits for ALL build phases)
|
||||
# =========================================================================
|
||||
publish-release:
|
||||
name: Publish draft release
|
||||
runs-on: ubuntu-latest
|
||||
environment: Production
|
||||
needs: [create-release, build-artifacts]
|
||||
if: needs.build-artifacts.result == 'success'
|
||||
needs: [prepare-release, create-release, build-desktop, build-docker]
|
||||
if: needs.build-desktop.result == 'success' && needs.build-docker.result == 'success'
|
||||
steps:
|
||||
- name: Validate required installer assets exist
|
||||
uses: actions/github-script@v7
|
||||
@@ -764,6 +844,33 @@ jobs:
|
||||
}
|
||||
core.info('All required installer assets are present.');
|
||||
|
||||
- name: Promote Docker image from staging to release tags
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: tinyhumansai/openhuman-core
|
||||
TAG: ${{ needs.prepare-release.outputs.tag }}
|
||||
run: |
|
||||
# Log in to GHCR
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||
|
||||
STAGING="${REGISTRY}/${IMAGE_NAME}:staging-${TAG}"
|
||||
VERSIONED="${REGISTRY}/${IMAGE_NAME}:${TAG}"
|
||||
LATEST="${REGISTRY}/${IMAGE_NAME}:latest"
|
||||
|
||||
echo "Promoting ${STAGING} → ${VERSIONED}, ${LATEST}"
|
||||
docker buildx imagetools create --tag "${VERSIONED}" --tag "${LATEST}" "${STAGING}"
|
||||
|
||||
- name: Append Docker pull instructions to release notes
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ needs.prepare-release.outputs.tag }}
|
||||
IMAGE_NAME: tinyhumansai/openhuman-core
|
||||
run: |
|
||||
gh release view "${TAG}" --repo tinyhumansai/openhuman --json body -q .body > /tmp/body.md
|
||||
printf '\n---\n\n### Docker\n\n```\ndocker pull ghcr.io/%s:%s\ndocker run -p 7788:7788 --env-file .env ghcr.io/%s:%s\n```\n' \
|
||||
"${IMAGE_NAME}" "${TAG}" "${IMAGE_NAME}" "${TAG}" >> /tmp/body.md
|
||||
gh release edit "${TAG}" --repo tinyhumansai/openhuman --notes-file /tmp/body.md
|
||||
|
||||
- name: Publish release
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
@@ -777,153 +884,19 @@ jobs:
|
||||
});
|
||||
core.info(`Published release ${releaseId}`);
|
||||
|
||||
# notify-discord:
|
||||
# name: Notify Discord about release
|
||||
# runs-on: ubuntu-latest
|
||||
# environment: Production
|
||||
# needs: [prepare-release, create-release, build-artifacts, publish-release]
|
||||
# if: needs.publish-release.result == 'success' && needs.build-artifacts.result == 'success'
|
||||
# steps:
|
||||
# - name: Post release notification to Discord
|
||||
# continue-on-error: true
|
||||
# uses: actions/github-script@v7
|
||||
# env:
|
||||
# DISCORD_RELEASE_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
|
||||
# RELEASE_ID: ${{ needs.create-release.outputs.release_id }}
|
||||
# RELEASE_TAG: ${{ needs.prepare-release.outputs.tag }}
|
||||
# with:
|
||||
# script: |
|
||||
# const webhookUrl = process.env.DISCORD_RELEASE_WEBHOOK_URL;
|
||||
# const releaseId = Number(process.env.RELEASE_ID);
|
||||
# const releaseTag = process.env.RELEASE_TAG;
|
||||
|
||||
# if (!webhookUrl) {
|
||||
# core.warning('DISCORD_RELEASE_WEBHOOK_URL is not configured; skipping Discord release notification.');
|
||||
# return;
|
||||
# }
|
||||
|
||||
# if (!Number.isFinite(releaseId) || releaseId <= 0) {
|
||||
# core.warning(`Invalid release id "${process.env.RELEASE_ID}"; skipping Discord release notification.`);
|
||||
# return;
|
||||
# }
|
||||
|
||||
# const { owner, repo } = context.repo;
|
||||
# const maxDescriptionLength = 3500;
|
||||
# const maxAssetLines = 8;
|
||||
|
||||
# function truncate(text, maxLength) {
|
||||
# if (!text) return '';
|
||||
# if (text.length <= maxLength) return text;
|
||||
# return `${text.slice(0, maxLength - 3)}...`;
|
||||
# }
|
||||
|
||||
# function stripMarkdownHeadings(text) {
|
||||
# return text
|
||||
# .replace(/^#{1,6}\s+/gm, '')
|
||||
# .replace(/\r/g, '')
|
||||
# .trim();
|
||||
# }
|
||||
|
||||
# const { data: release } = await github.rest.repos.getRelease({
|
||||
# owner,
|
||||
# repo,
|
||||
# release_id: releaseId,
|
||||
# });
|
||||
|
||||
# const releases = await github.paginate(github.rest.repos.listReleases, {
|
||||
# owner,
|
||||
# repo,
|
||||
# per_page: 100,
|
||||
# });
|
||||
# const sortedPublished = releases
|
||||
# .filter((item) => !item.draft)
|
||||
# .sort((a, b) => new Date(b.published_at || b.created_at || 0) - new Date(a.published_at || a.created_at || 0));
|
||||
# const currentIndex = sortedPublished.findIndex((item) => item.id === release.id);
|
||||
# const previousRelease = currentIndex >= 0 ? sortedPublished[currentIndex + 1] : null;
|
||||
# const compareUrl = previousRelease
|
||||
# ? `https://github.com/${owner}/${repo}/compare/${previousRelease.tag_name}...${release.tag_name}`
|
||||
# : null;
|
||||
|
||||
# const releaseTitle = release.name || release.tag_name || releaseTag;
|
||||
# const bodyText = stripMarkdownHeadings(release.body || '');
|
||||
# const summary = bodyText
|
||||
# ? truncate(bodyText, maxDescriptionLength)
|
||||
# : 'GitHub release published. See the release page for full notes.';
|
||||
|
||||
# const assetLines = (release.assets || [])
|
||||
# .slice(0, maxAssetLines)
|
||||
# .map((asset) => `- [${asset.name}](${asset.browser_download_url})`);
|
||||
# const extraAssetCount = Math.max((release.assets || []).length - maxAssetLines, 0);
|
||||
# if (extraAssetCount > 0) {
|
||||
# assetLines.push(`- ${extraAssetCount} more asset(s) available on the release page`);
|
||||
# }
|
||||
|
||||
# const fields = [
|
||||
# {
|
||||
# name: 'Version',
|
||||
# value: `\`${release.tag_name || releaseTag}\``,
|
||||
# inline: true,
|
||||
# },
|
||||
# {
|
||||
# name: 'Release',
|
||||
# value: `[Open on GitHub](${release.html_url})`,
|
||||
# inline: true,
|
||||
# },
|
||||
# ];
|
||||
|
||||
# if (compareUrl) {
|
||||
# fields.push({
|
||||
# name: 'Compare',
|
||||
# value: `[${previousRelease.tag_name}...${release.tag_name}](${compareUrl})`,
|
||||
# inline: true,
|
||||
# });
|
||||
# }
|
||||
|
||||
# if (assetLines.length > 0) {
|
||||
# fields.push({
|
||||
# name: 'Artifacts',
|
||||
# value: assetLines.join('\n'),
|
||||
# inline: false,
|
||||
# });
|
||||
# }
|
||||
|
||||
# const payload = {
|
||||
# content: `New GitHub release published for \`${owner}/${repo}\`: **${releaseTitle}**`,
|
||||
# embeds: [
|
||||
# {
|
||||
# title: releaseTitle,
|
||||
# url: release.html_url,
|
||||
# description: summary,
|
||||
# color: release.prerelease ? 0xf0ad4e : 0x2ecc71,
|
||||
# fields,
|
||||
# footer: {
|
||||
# text: release.prerelease ? 'Pre-release' : 'Published release',
|
||||
# },
|
||||
# timestamp: release.published_at || new Date().toISOString(),
|
||||
# },
|
||||
# ],
|
||||
# };
|
||||
|
||||
# const response = await fetch(webhookUrl, {
|
||||
# method: 'POST',
|
||||
# headers: { 'Content-Type': 'application/json' },
|
||||
# body: JSON.stringify(payload),
|
||||
# });
|
||||
|
||||
# if (!response.ok) {
|
||||
# const body = await response.text();
|
||||
# core.warning(`Discord notification failed: HTTP ${response.status} ${body.slice(0, 300)}`);
|
||||
# return;
|
||||
# }
|
||||
|
||||
# core.info(`Posted Discord release notification for ${release.tag_name || releaseTag}.`);
|
||||
|
||||
# =========================================================================
|
||||
# Cleanup: remove draft release + tag if ANY build phase failed
|
||||
# =========================================================================
|
||||
cleanup-failed-release:
|
||||
name: Remove release and tag if build failed
|
||||
runs-on: ubuntu-latest
|
||||
environment: Production
|
||||
needs: [prepare-release, create-release, build-artifacts]
|
||||
if: always() && needs.create-release.result == 'success' && (needs.build-artifacts.result == 'failure' || needs.build-artifacts.result == 'cancelled')
|
||||
needs: [prepare-release, create-release, build-desktop, build-docker]
|
||||
if: >-
|
||||
always()
|
||||
&& needs.create-release.result == 'success'
|
||||
&& (needs.build-desktop.result == 'failure' || needs.build-desktop.result == 'cancelled'
|
||||
|| needs.build-docker.result == 'failure' || needs.build-docker.result == 'cancelled')
|
||||
steps:
|
||||
- name: Delete GitHub release
|
||||
uses: actions/github-script@v7
|
||||
@@ -963,3 +936,23 @@ jobs:
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
- name: Delete staging Docker image
|
||||
continue-on-error: true
|
||||
env:
|
||||
TAG: ${{ needs.prepare-release.outputs.tag }}
|
||||
run: |
|
||||
PACKAGE="openhuman-core"
|
||||
STAGING_TAG="staging-${TAG}"
|
||||
echo "Attempting to delete staging Docker tag: ${STAGING_TAG}"
|
||||
# List package versions and find the staging tag
|
||||
VERSION_ID="$(gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"/orgs/tinyhumansai/packages/container/${PACKAGE}/versions" \
|
||||
--paginate --jq ".[] | select(.metadata.container.tags[] == \"${STAGING_TAG}\") | .id" 2>/dev/null | head -1)"
|
||||
if [ -n "$VERSION_ID" ]; then
|
||||
gh api -X DELETE "/orgs/tinyhumansai/packages/container/${PACKAGE}/versions/${VERSION_ID}" || true
|
||||
echo "Deleted staging image version ${VERSION_ID}"
|
||||
else
|
||||
echo "Staging tag ${STAGING_TAG} not found or already deleted"
|
||||
fi
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# ---------------------------------------------------------------------------
|
||||
# OpenHuman Core — multi-stage Docker build
|
||||
# Produces a minimal image running the `openhuman-core` binary (JSON-RPC server).
|
||||
#
|
||||
# Build: docker build -t openhuman-core .
|
||||
# Run: docker run -p 7788:7788 --env-file .env openhuman-core
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# ==========================================================================
|
||||
# Stage 1: Build the Rust binary
|
||||
# ==========================================================================
|
||||
FROM rust:1.93-bookworm AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# System dependencies required for compilation
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libssl-dev \
|
||||
clang \
|
||||
mold \
|
||||
ca-certificates \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Cache dependencies — copy only manifests first
|
||||
COPY Cargo.toml Cargo.lock rust-toolchain.toml ./
|
||||
# Create a dummy src to build deps
|
||||
RUN mkdir -p src && \
|
||||
echo 'fn main() {}' > src/main.rs && \
|
||||
echo 'pub fn run_core_from_args(_: &[String]) -> anyhow::Result<()> { Ok(()) }' > src/lib.rs && \
|
||||
cargo build --release --bin openhuman-core 2>/dev/null || true && \
|
||||
rm -rf src
|
||||
|
||||
# Copy actual source and build
|
||||
COPY src/ src/
|
||||
# Touch main.rs to force rebuild of our code (not deps)
|
||||
RUN touch src/main.rs src/lib.rs && \
|
||||
cargo build --release --bin openhuman-core
|
||||
|
||||
# ==========================================================================
|
||||
# Stage 2: Minimal runtime image
|
||||
# ==========================================================================
|
||||
FROM debian:bookworm-slim AS runtime
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
libssl3 \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Non-root user for security
|
||||
RUN useradd --create-home --shell /bin/bash openhuman
|
||||
USER openhuman
|
||||
WORKDIR /home/openhuman
|
||||
|
||||
# Copy the built binary
|
||||
COPY --from=builder /build/target/release/openhuman-core /usr/local/bin/openhuman-core
|
||||
|
||||
# Default workspace directory
|
||||
ENV OPENHUMAN_WORKSPACE=/home/openhuman/.openhuman
|
||||
# Bind to all interfaces so the container is reachable
|
||||
ENV OPENHUMAN_CORE_HOST=0.0.0.0
|
||||
ENV OPENHUMAN_CORE_PORT=7788
|
||||
ENV RUST_LOG=info
|
||||
|
||||
EXPOSE 7788
|
||||
|
||||
# Health check against the root endpoint
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||
CMD curl -sf http://localhost:7788/health || exit 1
|
||||
|
||||
ENTRYPOINT ["openhuman-core"]
|
||||
CMD ["serve"]
|
||||
+17
-3
@@ -38,6 +38,7 @@ pub fn run_from_cli_args(args: &[String]) -> Result<()> {
|
||||
|
||||
fn run_server_command(args: &[String]) -> Result<()> {
|
||||
let mut port: Option<u16> = None;
|
||||
let mut host: Option<String> = None;
|
||||
let mut socketio_enabled = true;
|
||||
let mut verbose = false;
|
||||
let mut i = 0usize;
|
||||
@@ -53,6 +54,14 @@ fn run_server_command(args: &[String]) -> Result<()> {
|
||||
);
|
||||
i += 2;
|
||||
}
|
||||
"--host" => {
|
||||
host = Some(
|
||||
args.get(i + 1)
|
||||
.ok_or_else(|| anyhow::anyhow!("missing value for --host"))?
|
||||
.clone(),
|
||||
);
|
||||
i += 2;
|
||||
}
|
||||
"--jsonrpc-only" => {
|
||||
socketio_enabled = false;
|
||||
i += 1;
|
||||
@@ -62,8 +71,11 @@ fn run_server_command(args: &[String]) -> Result<()> {
|
||||
i += 1;
|
||||
}
|
||||
"-h" | "--help" => {
|
||||
println!("Usage: openhuman run [--port <u16>] [--jsonrpc-only] [-v|--verbose]");
|
||||
println!("Usage: openhuman run [--host <addr>] [--port <u16>] [--jsonrpc-only] [-v|--verbose]");
|
||||
println!();
|
||||
println!(
|
||||
" --host <addr> Bind address (default: 127.0.0.1 or OPENHUMAN_CORE_HOST)"
|
||||
);
|
||||
println!(
|
||||
" --port <u16> Listen address port (default: 7788 or OPENHUMAN_CORE_PORT)"
|
||||
);
|
||||
@@ -82,7 +94,9 @@ fn run_server_command(args: &[String]) -> Result<()> {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()?;
|
||||
rt.block_on(async { crate::core::jsonrpc::run_server(port, socketio_enabled).await })?;
|
||||
rt.block_on(async {
|
||||
crate::core::jsonrpc::run_server(host.as_deref(), port, socketio_enabled).await
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -327,7 +341,7 @@ fn grouped_schemas() -> BTreeMap<String, Vec<ControllerSchema>> {
|
||||
fn print_general_help(grouped: &BTreeMap<String, Vec<ControllerSchema>>) {
|
||||
println!("OpenHuman core CLI\n");
|
||||
println!("Usage:");
|
||||
println!(" openhuman run [--port <u16>] [--jsonrpc-only] [--verbose]");
|
||||
println!(" openhuman run [--host <addr>] [--port <u16>] [--jsonrpc-only] [--verbose]");
|
||||
println!(" openhuman repl [--verbose] [--eval '<cmd>'] [--batch]");
|
||||
println!(" openhuman call --method <name> [--params '<json>']");
|
||||
println!(" openhuman <namespace> <function> [--param value ...]\n");
|
||||
|
||||
+53
-4
@@ -416,11 +416,60 @@ fn core_port() -> u16 {
|
||||
.unwrap_or(7788)
|
||||
}
|
||||
|
||||
pub async fn run_server(port: Option<u16>, socketio_enabled: bool) -> anyhow::Result<()> {
|
||||
fn core_host() -> String {
|
||||
std::env::var("OPENHUMAN_CORE_HOST")
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or_else(|| "127.0.0.1".to_string())
|
||||
}
|
||||
|
||||
pub async fn run_server(
|
||||
host: Option<&str>,
|
||||
port: Option<u16>,
|
||||
socketio_enabled: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let _ = all::all_registered_controllers();
|
||||
let port = port.unwrap_or_else(core_port);
|
||||
let bind_addr = format!("127.0.0.1:{port}");
|
||||
let listener = tokio::net::TcpListener::bind(&bind_addr).await?;
|
||||
|
||||
let (resolved_port, port_source) = match port {
|
||||
Some(p) => (p, "CLI --port"),
|
||||
None => (
|
||||
core_port(),
|
||||
if std::env::var("OPENHUMAN_CORE_PORT").is_ok() {
|
||||
"env OPENHUMAN_CORE_PORT"
|
||||
} else {
|
||||
"default"
|
||||
},
|
||||
),
|
||||
};
|
||||
let (resolved_host, host_source) = match host {
|
||||
Some(h) => (h.to_string(), "CLI --host"),
|
||||
None => (
|
||||
core_host(),
|
||||
if std::env::var("OPENHUMAN_CORE_HOST")
|
||||
.ok()
|
||||
.filter(|s| !s.is_empty())
|
||||
.is_some()
|
||||
{
|
||||
"env OPENHUMAN_CORE_HOST"
|
||||
} else {
|
||||
"default"
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
log::debug!(
|
||||
"[core] Bind resolution: host={resolved_host} (from {host_source}), port={resolved_port} (from {port_source})"
|
||||
);
|
||||
|
||||
let port = resolved_port;
|
||||
let host = resolved_host;
|
||||
let bind_addr = format!("{host}:{port}");
|
||||
let listener = tokio::net::TcpListener::bind((host.as_str(), port))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
log::error!("[core] Failed to bind to {bind_addr}: {e}");
|
||||
e
|
||||
})?;
|
||||
|
||||
let app = build_core_http_router(socketio_enabled);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user