Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
.git
.github
.aionui
.claude
.codex
.gemini
.specify
.superpowers
.worktree
.worktrees

node_modules
out
dist
dist-server
.git
.github
dist-web-cli
coverage
data
resources
tests
docs
examples
homebrew
mobile

.env
.env.*
*.log
*.tmp
.DS_Store
55 changes: 55 additions & 0 deletions .github/workflows/build-and-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,61 @@ jobs:
skip_code_quality: true
secrets: inherit

docker-image:
name: Publish Docker Image
runs-on: ubuntu-latest
needs: code-quality
if: needs.code-quality.result == 'success' && (github.ref == 'refs/heads/dev' || (startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-dev-')))
permissions:
actions: write
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Resolve lowercase image name
id: image
shell: bash
run: echo "name=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/aionui" >> "$GITHUB_OUTPUT"

- name: Generate image metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.image.outputs.name }}
tags: |
type=ref,event=tag
type=sha,prefix=sha-
type=raw,value=dev,enable=${{ github.ref == 'refs/heads/dev' }}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-dev-') && !contains(github.ref, 'alpha') && !contains(github.ref, 'beta') && !contains(github.ref, 'rc') }}

- name: Build and publish multi-architecture image
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=aionui-docker
cache-to: type=gha,mode=max,scope=aionui-docker
provenance: mode=max
sbom: true

# 自动重试 workflow(当构建失败时)
auto-retry-workflow:
name: Auto Retry on Build Failure
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -238,3 +238,6 @@ graphify-out/
.analysis/
temp/
.playwright-mcp/

# Local multi-user aioncore for Docker builds (not published releases)
docker/prebuilt/aioncore
119 changes: 85 additions & 34 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,47 +1,98 @@
FROM node:20-slim AS builder
WORKDIR /app

# Install bun
RUN npm install -g bun
# syntax=docker/dockerfile:1

# Install all dependencies (including devDeps for build)
COPY package.json bun.lock ./
COPY patches/ ./patches/
RUN bun install --ignore-scripts
ARG NODE_VERSION=22.23.2-bookworm-slim

# Copy source
COPY . .
FROM node:${NODE_VERSION} AS builder

# Build renderer (no Electron needed) and server bundle
RUN bun run build:renderer:web
RUN node scripts/build-server.mjs
ARG BUN_VERSION=1.3.14

# ---- Runtime image ----
FROM oven/bun:latest AS runtime
WORKDIR /app

# officecli (the Office preview component, auto-installed at runtime by the
# backend) is a .NET binary that aborts on startup without ICU, and Debian
# base images don't ship it. libicu-dev is version-agnostic so it keeps
# resolving the right libicuNN when the base image bumps Debian releases.
# AionCore is downloaded while packaging. ICU is also needed when it prepares
# the managed Office tooling bundled into the final Web CLI artifact.
RUN apt-get update \
&& apt-get install -y --no-install-recommends libicu-dev \
&& apt-get install -y --no-install-recommends ca-certificates curl gzip libicu-dev tar \
&& rm -rf /var/lib/apt/lists/*
RUN npm install --global --no-audit --no-fund "bun@${BUN_VERSION}"

# Copy only build artifacts and production deps
COPY --from=builder /app/dist-server ./dist-server
COPY --from=builder /app/out/renderer ./out/renderer
# Install against the complete workspace manifest set before copying source so
# dependency installation remains cached when only application code changes.
COPY package.json bun.lock ./
COPY patches/ ./patches/
RUN bun install --production --ignore-scripts
COPY patches ./patches
COPY packages/desktop/package.json ./packages/desktop/package.json
COPY packages/shared-scripts/package.json ./packages/shared-scripts/package.json
COPY packages/web-cli/package.json ./packages/web-cli/package.json
COPY packages/web-host/package.json ./packages/web-host/package.json
RUN bun install --frozen-lockfile --ignore-scripts

COPY packages ./packages
COPY public ./public
COPY scripts ./scripts
COPY tsconfig.json uno.config.ts ./
# Optional Linux aioncore for local/dev builds when the pinned release is not
# published yet. Place the binary at docker/prebuilt/aioncore before building.
COPY docker/prebuilt ./docker/prebuilt

ENV NODE_OPTIONS=--max-old-space-size=8192

# Build the browser assets, then create the same standalone Web CLI artifact
# that the release workflow smoke-tests on Debian. The artifact includes the
# compiled launcher, the SPA, and the architecture-matched AionCore backend.
# Prefer a prebuilt multi-user aioncore when present; otherwise download the pin.
RUN bun run package
RUN if [ -x /app/docker/prebuilt/aioncore ]; then \
export AIONUI_BACKEND_LOCAL_BINARY=/app/docker/prebuilt/aioncore; \
echo "Using prebuilt aioncore from docker/prebuilt/aioncore"; \
fi \
&& node scripts/pack-web-cli.js
RUN WEB_CLI_TARBALL="$(find dist-web-cli -maxdepth 1 -name '*.tar.gz' -print -quit)" \
&& test -n "${WEB_CLI_TARBALL}" \
&& bash scripts/smoke-test-web-cli.sh "${WEB_CLI_TARBALL}"

FROM debian:bookworm-slim AS runtime

ARG AIONUI_UID=10001
ARG AIONUI_GID=10001

LABEL org.opencontainers.image.title="AionUi WebUI" \
org.opencontainers.image.description="Headless AionUi WebUI with bundled AionCore" \
org.opencontainers.image.source="https://github.com/iOfficeAI/AionUi" \
org.opencontainers.image.licenses="Apache-2.0"

# curl powers the health check; ICU is required by OfficeCLI previews. Git and
# OpenSSH keep source-control workspaces usable from the containerized agent.
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ca-certificates curl git libicu-dev openssh-client tzdata \
&& rm -rf /var/lib/apt/lists/* \
&& (getent group "${AIONUI_GID}" > /dev/null || groupadd --gid "${AIONUI_GID}" aionui) \
&& useradd --uid "${AIONUI_UID}" --gid "${AIONUI_GID}" --create-home --shell /bin/bash aionui \
&& mkdir -p /data/home /workspace \
&& chown -R "${AIONUI_UID}:${AIONUI_GID}" /data /workspace

COPY --from=builder /app/dist-web-cli/staging/aionui-web /opt/aionui
RUN chmod -R go-w /opt/aionui

ENV NODE_ENV=production \
PORT=25808 \
AIONUI_ALLOW_REMOTE=true \
AIONUI_DATA_DIR=/data \
AIONUI_LOG_DIR=/data/logs \
AIONUI_WORK_DIR=/workspace \
AIONUI_BOOTSTRAP_WORKSPACE=/workspace \
AIONUI_LOG_LEVEL=info \
AIONUI_OPEN_BROWSER=false \
HOME=/data/home \
PATH="/opt/aionui:${PATH}"

WORKDIR /workspace
USER aionui

ENV PORT=3000
ENV NODE_ENV=production
ENV ALLOW_REMOTE=true
ENV DATA_DIR=/data
VOLUME ["/data", "/workspace"]
EXPOSE 25808

# SQLite data volume — mount with: -v $(pwd)/data:/data
VOLUME ["/data"]
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=45s --retries=5 \
CMD curl --fail --silent --show-error "http://127.0.0.1:${AIONUI_PORT:-${PORT:-25808}}/api/auth/status" > /dev/null || exit 1

CMD ["bun", "dist-server/server.mjs"]
STOPSIGNAL SIGTERM
ENTRYPOINT ["aionui-web"]
CMD ["start", "--no-open"]
65 changes: 65 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Local development / source build.
# Builds the image from this repository's Dockerfile instead of pulling a registry image.
#
# docker compose -f docker-compose.dev.yml up --build -d
#
# Match host bind-mount ownership on Linux when needed:
# AIONUI_UID=$(id -u) AIONUI_GID=$(id -g) AIONUI_WORKSPACE_PATH=$PWD/workspace \
# docker compose -f docker-compose.dev.yml up --build -d

name: aionui-dev

services:
aionui:
image: ${AIONUI_IMAGE:-aionui:local}
pull_policy: ${AIONUI_PULL_POLICY:-build}
build:
context: .
dockerfile: Dockerfile
args:
NODE_VERSION: ${NODE_VERSION:-22.23.2-bookworm-slim}
BUN_VERSION: ${BUN_VERSION:-1.3.14}
AIONUI_UID: ${AIONUI_UID:-10001}
AIONUI_GID: ${AIONUI_GID:-10001}
init: true
read_only: true
restart: unless-stopped
stop_grace_period: 20s
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:size=512m,mode=1777
environment:
NODE_ENV: production
AIONUI_PORT: 25808
AIONUI_ALLOW_REMOTE: 'true'
AIONUI_DATA_DIR: /data
AIONUI_LOG_DIR: /data/logs
AIONUI_WORK_DIR: /workspace
AIONUI_BOOTSTRAP_WORKSPACE: /workspace
AIONUI_LOG_LEVEL: ${AIONUI_LOG_LEVEL:-info}
AIONUI_OPEN_BROWSER: 'false'
AIONUI_HTTPS: ${AIONUI_HTTPS:-false}
AIONUI_TRUST_PROXY: ${AIONUI_TRUST_PROXY:-false}
AIONUI_INITIAL_ADMIN_USERNAME: ${AIONUI_INITIAL_ADMIN_USERNAME:-admin}
AIONUI_INITIAL_ADMIN_CREDENTIALS_FILE: /data/initial-admin-credentials.json
TZ: ${TZ:-UTC}
ports:
- '${AIONUI_BIND_ADDRESS:-127.0.0.1}:${AIONUI_HOST_PORT:-25808}:25808'
volumes:
- aionui-data:/data
- ${AIONUI_WORKSPACE_PATH:-aionui-workspace}:/workspace
healthcheck:
test:
- CMD-SHELL
- 'curl --fail --silent --show-error "http://127.0.0.1:25808/api/auth/status" > /dev/null'
interval: 30s
timeout: 5s
start_period: 45s
retries: 5

volumes:
aionui-data:
aionui-workspace:
56 changes: 56 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Production / release deployment.
# Pulls the published image from the upstream project (iOfficeAI), not a personal fork.
#
# docker compose -f docker-compose.yml up -d
#
# Override the tag when needed:
# AIONUI_IMAGE_TAG=v2.1.53 docker compose -f docker-compose.yml up -d

name: aionui

services:
aionui:
image: ${AIONUI_IMAGE:-ghcr.io/iofficeai/aionui}:${AIONUI_IMAGE_TAG:-latest}
pull_policy: ${AIONUI_PULL_POLICY:-always}
init: true
read_only: true
restart: unless-stopped
stop_grace_period: 20s
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp:size=512m,mode=1777
environment:
NODE_ENV: production
AIONUI_PORT: 25808
AIONUI_ALLOW_REMOTE: 'true'
AIONUI_DATA_DIR: /data
AIONUI_LOG_DIR: /data/logs
AIONUI_WORK_DIR: /workspace
AIONUI_BOOTSTRAP_WORKSPACE: /workspace
AIONUI_LOG_LEVEL: ${AIONUI_LOG_LEVEL:-info}
AIONUI_OPEN_BROWSER: 'false'
AIONUI_HTTPS: ${AIONUI_HTTPS:-false}
AIONUI_TRUST_PROXY: ${AIONUI_TRUST_PROXY:-false}
AIONUI_INITIAL_ADMIN_USERNAME: ${AIONUI_INITIAL_ADMIN_USERNAME:-admin}
AIONUI_INITIAL_ADMIN_CREDENTIALS_FILE: /data/initial-admin-credentials.json
TZ: ${TZ:-UTC}
ports:
- '${AIONUI_BIND_ADDRESS:-127.0.0.1}:${AIONUI_HOST_PORT:-25808}:25808'
volumes:
- aionui-data:/data
- ${AIONUI_WORKSPACE_PATH:-aionui-workspace}:/workspace
healthcheck:
test:
- CMD-SHELL
- 'curl --fail --silent --show-error "http://127.0.0.1:25808/api/auth/status" > /dev/null'
interval: 30s
timeout: 5s
start_period: 45s
retries: 5

volumes:
aionui-data:
aionui-workspace:
11 changes: 11 additions & 0 deletions docker/prebuilt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Optional local AionCore for Docker builds

Place a Linux `aioncore` binary here when the pinned `aioncoreVersion` in
`package.json` is not published yet. The Dockerfile uses it if present:

```text
docker/prebuilt/aioncore
```

Do not commit the binary. After AionCore multi-user is released, remove this
file and let packaging download the pin from GitHub Releases instead.
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Documentation is organized by reader intent, not by document type.
- New to the project? Start with [`architecture/overview.md`](architecture/overview.md).
- Setting up a dev environment? See [`contributing/development.md`](contributing/development.md).
- Writing code? The entry point for code-style, linting, formatting, and commit rules is [`AGENTS.md`](../AGENTS.md) at the repo root.
- Deploying a server? [`guides/deploy-server.md`](guides/deploy-server.md).
- Deploying with Docker? [`guides/docker.md`](guides/docker.md). Using a packaged `.deb` on a server? [`guides/deploy-server.md`](guides/deploy-server.md).

## Where to put new docs

Expand Down
2 changes: 2 additions & 0 deletions docs/guides/deploy-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Deploy AionUi WebUI on headless Linux servers — cloud VMs, Kubernetes Pods, and containers — with proxy auto-fallback support.

> **Recommended for new container deployments:** use the Electron-free [Docker Compose guide](docker.md) (`docker-compose.yml` pulls the GHCR release image; `docker-compose.dev.yml` builds from source). The guide below describes the packaged Electron application with Xvfb and remains useful for `.deb`-based server installations.

**Translations**: [中文版](#中文版--chinese-version) below.

## Table of Contents
Expand Down
Loading