feat: OpenMesh 基础平台与 MD/PDF 转换技能
- 后端: coworker 智能体框架, WS API, 文件上传, 附件处理 - 前端: Open WebUI, 文件全量走 upload API (含 MD/TXT/JSON 等文本类) - 技能: md-to-office (pandoc + wkhtmltopdf) - 修复: 上传文件路径丢失, Agent 搜索浪费, 输出文件跑到 uploads/ - 打包: PyInstaller one-dir, 预打包 pandoc/wkhtmltopdf/chromium
This commit is contained in:
195
.github/workflows/release.yml
vendored
Normal file
195
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
# Desktop release builds — macOS (.dmg, arm64 + Intel) and Windows (.msi + NSIS .exe).
|
||||
#
|
||||
# CI calls the SAME scripts developers run locally (packaging/build_dmg.sh and
|
||||
# build_windows.ps1); this file only provisions the toolchain (Node, Rust, a Python venv at
|
||||
# .venv with PyInstaller) and publishes the results.
|
||||
#
|
||||
# Triggers:
|
||||
# - tag push `v*` → builds all targets and attaches them to a DRAFT GitHub Release
|
||||
# (review, then publish by hand).
|
||||
# - manual run → builds and uploads workflow artifacts only (no release).
|
||||
#
|
||||
# Each installer is uploaded twice: once with its versioned name (archive) and once with a
|
||||
# stable name (OpenWorker-macos-arm64.dmg, …) so the website can link to
|
||||
# github.com/<repo>/releases/latest/download/<stable-name>
|
||||
# and never need updating.
|
||||
#
|
||||
# macOS signing + notarization: Tauri's bundler handles both during `tauri build` when the
|
||||
# APPLE_* env vars are present (import cert → sign app + sidecar with hardened runtime →
|
||||
# notarize via notarytool → staple). Driven by repo secrets:
|
||||
# APPLE_CERTIFICATE base64 .p12 (Developer ID Application cert + key)
|
||||
# APPLE_CERTIFICATE_PASSWORD the .p12 export password
|
||||
# APPLE_SIGNING_IDENTITY e.g. "Developer ID Application: Name (TEAMID)"
|
||||
# APPLE_API_KEY_CONTENT base64 App Store Connect API .p8 (notarytool)
|
||||
# APPLE_API_KEY the API key id
|
||||
# APPLE_API_ISSUER the API issuer id
|
||||
# When the secrets are absent (forks, scratch runs) the build degrades to unsigned —
|
||||
# installable via `xattr -cr`. Windows remains unsigned (Authenticode is a later step).
|
||||
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*", "app-v*"]
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-latest # Apple Silicon
|
||||
slug: macos-arm64
|
||||
# Intel macOS: macos-13 retired in Dec 2025; macos-15-intel replaced it and is
|
||||
# the LAST x86_64 image Actions will offer (available until Aug 2027). Builds
|
||||
# natively — the sidecar is a PyInstaller freeze, which cannot cross-compile.
|
||||
- os: macos-15-intel
|
||||
slug: macos-x64
|
||||
- os: windows-latest
|
||||
slug: windows
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: surfaces/gui/package-lock.json
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: surfaces/gui/src-tauri
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Set up the sidecar venv (.venv)
|
||||
# The build scripts expect a venv at .venv with the package + PyInstaller.
|
||||
# typer/tzdata are build-time-only (PyInstaller walks mcp.cli, which needs typer;
|
||||
# tzdata ships zoneinfo for Windows). aisuite installs like any other dependency
|
||||
# (git-pinned in pyproject.toml).
|
||||
run: |
|
||||
python -m venv .venv
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then VPY=.venv/Scripts/python; else VPY=.venv/bin/python; fi
|
||||
"$VPY" -m pip install --upgrade pip
|
||||
"$VPY" -m pip install -e ".[bedrock]" pyinstaller typer tzdata
|
||||
"$VPY" -c "import aisuite, coworker" # fail fast if either import breaks
|
||||
|
||||
- name: npm ci
|
||||
working-directory: surfaces/gui
|
||||
run: npm ci
|
||||
|
||||
- name: Build .dmg (macOS)
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
APPLE_API_KEY_CONTENT: ${{ secrets.APPLE_API_KEY_CONTENT }}
|
||||
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
# Auto-update artifact signing (minisign, separate from Apple signing). Absent →
|
||||
# the build script skips updater artifacts with a warning (fork/scratch runs).
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: |
|
||||
# Unset empty APPLE_* vars so runs without secrets stay cleanly unsigned
|
||||
# (Tauri treats a present-but-empty var as a config error).
|
||||
for v in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD APPLE_SIGNING_IDENTITY APPLE_API_KEY APPLE_API_ISSUER; do
|
||||
[ -n "$(eval echo "\${$v:-}")" ] || unset "$v"
|
||||
done
|
||||
if [ -n "${APPLE_API_KEY_CONTENT:-}" ]; then
|
||||
echo "$APPLE_API_KEY_CONTENT" | base64 -d > "$RUNNER_TEMP/AuthKey.p8"
|
||||
export APPLE_API_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
|
||||
fi
|
||||
unset APPLE_API_KEY_CONTENT
|
||||
bash packaging/build_dmg.sh
|
||||
|
||||
- name: Build .msi + NSIS .exe (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
run: ./packaging/build_windows.ps1
|
||||
|
||||
- name: Stage artifacts (versioned + stable names)
|
||||
run: |
|
||||
mkdir -p out
|
||||
BUNDLE=surfaces/gui/src-tauri/target/release/bundle
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
cp "$BUNDLE"/nsis/*.exe out/
|
||||
cp "$BUNDLE"/nsis/*.exe out/OpenWorker-windows-setup.exe
|
||||
cp "$BUNDLE"/msi/*.msi out/
|
||||
cp "$BUNDLE"/msi/*.msi out/OpenWorker-windows.msi
|
||||
# Updater signature for the NSIS installer (present only when the updater key
|
||||
# secret is configured). The .sig signs CONTENT, so the stable rename is safe.
|
||||
SIG=$(ls "$BUNDLE"/nsis/*.exe.sig 2>/dev/null | head -1 || true)
|
||||
[ -n "$SIG" ] && cp "$SIG" out/OpenWorker-windows-setup.exe.sig
|
||||
else
|
||||
cp "$BUNDLE"/dmg/*.dmg out/
|
||||
cp "$BUNDLE"/dmg/*.dmg out/OpenWorker-${{ matrix.slug }}.dmg
|
||||
# macOS updater artifact: the signed .app tarball the installed app swaps in.
|
||||
if [ -f "$BUNDLE"/macos/OpenWorker.app.tar.gz ]; then
|
||||
cp "$BUNDLE"/macos/OpenWorker.app.tar.gz out/OpenWorker-${{ matrix.slug }}.app.tar.gz
|
||||
cp "$BUNDLE"/macos/OpenWorker.app.tar.gz.sig out/OpenWorker-${{ matrix.slug }}.app.tar.gz.sig
|
||||
fi
|
||||
fi
|
||||
ls -la out
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.slug }}
|
||||
path: out/*
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist
|
||||
merge-multiple: true
|
||||
|
||||
- name: Compose the auto-update manifest (latest.json)
|
||||
# Shipped apps poll releases/latest/download/latest.json (via the branded
|
||||
# download.openworker.com redirect) — publishing this release IS pushing the
|
||||
# update. The tag must match tauri.conf.json's version or installed apps would
|
||||
# see a permanent phantom update; fail loudly on drift. Runs only when signed
|
||||
# updater artifacts exist (i.e. the TAURI_SIGNING_PRIVATE_KEY secret is set).
|
||||
run: |
|
||||
TAG="${GITHUB_REF_NAME}"
|
||||
CONF_VERSION=$(python3 -c "import json; print(json.load(open('surfaces/gui/src-tauri/tauri.conf.json'))['version'])")
|
||||
if [ "${TAG#v}" != "$CONF_VERSION" ]; then
|
||||
echo "::error::tag $TAG != tauri.conf.json version $CONF_VERSION — bump the config before tagging"
|
||||
exit 1
|
||||
fi
|
||||
if ls dist/*.sig >/dev/null 2>&1; then
|
||||
python3 packaging/make_update_manifest.py \
|
||||
--version "${TAG#v}" --tag "$TAG" --repo "$GITHUB_REPOSITORY" \
|
||||
--dist dist --out dist/latest.json \
|
||||
--notes "OpenWorker ${TAG#v}"
|
||||
else
|
||||
echo "::warning::no updater signatures in dist/ — release ships WITHOUT auto-update manifest"
|
||||
fi
|
||||
|
||||
- uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
draft: true
|
||||
files: dist/*
|
||||
generate_release_notes: true
|
||||
Reference in New Issue
Block a user