Skip to content
Merged
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
89 changes: 59 additions & 30 deletions .github/workflows/sync-main-to-develop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ name: Sync Main Into Develop
# `workflow_run` — when Auto Tag starts Release with GITHUB_TOKEN, those
# events often never create a Sync run. workflow_dispatch remains for
# manual catch-up; workflow_run / release are kept as best-effort backups.
#
# Strategy: keep main an ancestor of develop. Try merge first; on conflict,
# rebuild on main and open a PR (develop is often branch-protected against
# force-push). Never use head=main merge PRs — that pattern conflicts when
# main was updated via a bulk develop merge (#150-style divergence).
on:
workflow_run:
workflows:
Expand Down Expand Up @@ -37,60 +42,84 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: develop

- name: Open or merge sync PR
- name: Sync develop onto main
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ github.event.release.tag_name || github.event.workflow_run.head_branch || 'manual' }}
run: |
set -euo pipefail
version="${TAG#v}"
sync_branch="chore/sync-develop-after-${version}"
git fetch origin main develop

if git merge-base --is-ancestor origin/main origin/develop; then
echo "develop already contains main tip; nothing to sync."
exit 0
fi

# Close stale main→develop merge PRs (legacy sync path).
for pr in $(gh pr list --base develop --head main --state open --json number --jq '.[].number'); do
gh pr close "$pr" --comment "Superseded by automated sync (main must stay an ancestor of develop)."
done

saved_develop="$(git rev-parse origin/develop)"
git checkout -B develop origin/develop
merge_msg="chore: sync develop onto main after ${version}"

if git merge origin/main --no-edit -m "${merge_msg}"; then
if git push origin develop; then
echo "Merged main into develop."
exit 0
fi
echo "Merge succeeded locally but push failed (branch protection?) — opening PR."
git checkout -B "${sync_branch}"
git push -u origin "${sync_branch}" --force
else
git merge --abort
echo "Merge conflict — rebuilding develop on main tip."

git diff --name-only origin/main "${saved_develop}" > /tmp/diff_files.txt || true
git checkout -B "${sync_branch}" origin/main

grep -v -E '^(pyproject\.toml|src/octop/__init__.py|CHANGELOG\.md|uv\.lock)$' \
/tmp/diff_files.txt > /tmp/feature_files.txt || true

if [ -s /tmp/feature_files.txt ]; then
# shellcheck disable=SC2046
git checkout "${saved_develop}" -- $(cat /tmp/feature_files.txt)
git commit -m "${merge_msg}"
fi

git push -u origin "${sync_branch}" --force
fi

existing="$(
gh pr list \
--base develop \
--head main \
--head "${sync_branch}" \
--state open \
--json number \
--jq '.[0].number // empty'
)"

if [ -z "$existing" ]; then
url="$(
gh pr create \
--base develop \
--head main \
--title "chore: sync main into develop after ${version}" \
--body "Post-release sync so develop contains the shipped main tip (${TAG})."
)"
echo "Created sync PR: ${url}"
existing="$(
gh pr list \
--base develop \
--head main \
--state open \
--json number \
--jq '.[0].number // empty'
)"
else
if [ -n "$existing" ]; then
echo "Sync PR already open: #${existing}"
gh pr view "$existing" --json url --jq .url
exit 0
fi

if [ -z "$existing" ]; then
echo "::error::Failed to resolve sync PR number."
exit 1
fi
url="$(
gh pr create \
--base develop \
--head "${sync_branch}" \
--title "chore: sync develop onto main after ${version}" \
--body "Post-release sync so \`main\` stays an ancestor of \`develop\` (${TAG}). Merge when CI is green."
)"
echo "Created sync PR: ${url}"

# Prefer merge commit (not squash) so main stays an ancestor of develop.
if gh pr merge "$existing" --merge --delete-branch=false; then
echo "Merged #${existing} into develop."
if gh pr merge "$(basename "${url}")" --merge --delete-branch=false 2>/dev/null; then
echo "Auto-merged sync PR."
else
echo "::warning::Could not auto-merge #${existing} (conflicts or branch protection). Merge manually."
gh pr view "$existing" --json url --jq .url
echo "::notice::Sync PR needs manual merge (conflicts or branch protection)."
fi
6 changes: 4 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,10 @@ hotfix/* ──PR──► main (+ tag) and ──PR──► develop

**Rules**

- Never push `develop` directly onto `main` — all merges via PR.
- Release sequence: cut `release/*` from latest `develop` → PR into `main` → **tag `v*` on main tip only after merge** → delete `release/*` → sync `main` → `develop` when needed.
- Never push `develop` directly onto `main` — ship via `release/*` → `main` (or hotfix → `main`) only. Do **not** bulk-merge `develop` → `main`; it forks history and breaks post-release sync.
- Merge `release/*` → `main` with a **merge commit** (not squash) so `main` stays reconcilable with `develop`.
- Release sequence: cut `release/*` from latest `develop` → PR into `main` → **tag `v*` on main tip only after merge** → delete `release/*` → Actions syncs `main` → `develop` (`sync-main-to-develop.yml`; opens `chore/sync-develop-after-*` on conflict / branch protection).
- Keep **`main` an ancestor of `develop`** after every release. Do not use legacy `head=main` → `develop` sync PRs.
- Do **not** push a production tag from a release/feature branch before it is on `main`.
- Hotfix: branch from `main`, PR to `main` (and tag if shipping), then PR into `develop`.
- Day-to-day feature work: branch from `develop`, open PR **into `develop`** (not `main`).
Expand Down
25 changes: 15 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@

## [Unreleased]

## [0.9.19] - 2026-08-05

### 新增
- 高级设置「更新」页提供按安装方式升级说明与一键检查升级双栏布局
- 高级设置 HTTPS 页优化签发状态与预检结果展示
- 登录页滑动验证控件;侧栏与 Agent 资料抽屉 UI 优化
- 401 会话过期时通过 React Router 跳转登录,避免整页 reload 导致 lazy chunk 白屏
- 聊天历史 API 返回 ``turn_active``,重连客户端可据此 re-subscribe WebSocket 恢复流式输出
- 局部 ``root_dir`` 下 Linux bubblewrap execute jail(``POST /api/filesystem/ensure-bwrap``、仪表盘 root 目录树 mkdir/rename)
- 虚拟工作区路径 I/O:host 绝对路径经 ``file://`` 与 ``BackendWorkspace`` failback 对齐
- 登录页滑动验证控件;侧栏与 Agent 资料抽屉 UI 优化 (#170)
- 聊天历史 API 返回 `turn_active`,重连客户端可 re-subscribe WebSocket 恢复流式输出 (#168, #157)
- Workbench 与聊天 Dock 共用同一 terminal 会话;旧式硬切会话标题迁移为带省略号的裁剪标题 (#157)
- 局部 `root_dir` 下 Linux bubblewrap execute jail(`POST /api/filesystem/ensure-bwrap`、仪表盘 root 目录树 mkdir/rename)(#167)
- 虚拟工作区路径 I/O:host 绝对路径经 `file://` 与 `BackendWorkspace` failback 对齐 (#167)
- 高级设置「更新」页提供按安装方式升级说明与一键检查升级双栏布局;HTTPS 页优化签发状态与预检展示 (#143)

### 修复
- 401 会话过期时通过 React Router 跳转登录,避免整页 reload 导致 lazy chunk 白屏 (#169)

### 变更
- `make all` 先执行前后端 `format-all`(Ruff + Prettier);pre-commit 在 format 后回写已暂存文件并构建 dashboard
- harness runtime 诊断日志写入 ``~/.octop/logs``(与 ``octop.log`` 并排),不再落到各 agent workspace 的 ``logs/``;行内带 ``[agent=…]``
- 依赖 ``orcakit-harness-agent>=0.9.19``、``harness-gateway>=0.9.1``(scoped root execute jail)
- `make all` 先执行前后端 `format-all`(Ruff + Prettier);pre-commit 在 format 后回写已暂存文件并构建 dashboard (#143)
- harness runtime 诊断日志写入 `~/.octop/logs`(与 `octop.log` 并排),不再落到各 agent workspace 的 `logs/`;行内带 `[agent=…]`
- 依赖 `orcakit-harness-agent>=0.9.19`、`harness-gateway>=0.9.1`(scoped root execute jail)
- 企业微信客户群二维码与文档有效期更新至 2026-08-08 (#149)

## [0.9.18] - 2026-08-02

Expand Down
18 changes: 14 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ feature/* ──PR──► develop ──► release/x.y.z ──PR──► ma
hotfix/* ──PR──► main (+ tag) and ──PR──► develop
```

**Rules:** never push `develop` directly to `main` (PR only). Production `v*` tags are created **on `main` after** the release PR merges — not on the release branch before merge.
**Rules:**

- Never push `develop` directly to `main` — ship only via `release/x.y.z` → `main` (or hotfix → `main`). Do **not** open `develop` → `main` bulk merges; they fork history and break post-release sync.
- Merge `release/x.y.z` → `main` with a **merge commit** (not squash). Squash drops shared ancestry with `develop`.
- Production `v*` tags are created **on `main` after** the release PR merges — not on the release branch before merge.
- After a release, `main` must stay an **ancestor** of `develop`. GitHub Actions runs `sync-main-to-develop.yml` (merge first; on conflict, a `chore/sync-develop-after-*` PR). Do not open legacy `head=main` → `develop` PRs.

## Pull requests

Expand All @@ -67,7 +72,7 @@ See [AGENTS.md](AGENTS.md) for module boundaries and coding conventions.
1. Cut `release/x.y.z` from latest `develop` (version bump + CHANGELOG on that branch)
2. Open PR: `release/x.y.z` → `main` and merge when green
3. Tag `v<version>` on **main tip** and push — GitHub Actions builds, publishes to PyPI, and creates the GitHub Release
4. Delete `release/x.y.z`; if needed, open PR `main` → `develop` to sync
4. Delete `release/x.y.z`; Actions syncs `main` → `develop` (or opens `chore/sync-develop-after-*` if merge conflicts / branch protection)

Agent-assisted publish: `.cursor/skills/publish` (`/publish <version>`).

Expand Down Expand Up @@ -109,7 +114,12 @@ make check-all # 全栈质量门禁
| `release/x.y.z` | 临时发版分支;发版完成后删除 |
| `hotfix/*` | 从 `main` 紧急修复;合入 `main` 后再合回 `develop` |

**规则:** 禁止将 `develop` 直接推送到 `main`(必须走 PR)。生产 `v*` tag 仅在 release PR **合入 `main` 之后**打在 main tip 上。
**规则:**

- 禁止 `develop` 直推/直 merge 到 `main` — 发版必须走 `release/x.y.z` → `main`(或 hotfix → `main`)。不要开 `develop` → `main` 大包 PR,否则历史分叉、发版后 sync 必冲突。
- `release/x.y.z` → `main` 请用 **merge commit** 合并,不要 squash。
- 生产 `v*` tag 仅在 release PR **合入 `main` 之后**打在 main tip 上。
- 发版后 `main` 必须是 `develop` 的祖先;由 Actions `sync-main-to-develop.yml` 自动 sync(冲突或分支保护时会开 `chore/sync-develop-after-*` PR)。不要再用 `head=main` → `develop` 的老 sync PR。

## 提交流程

Expand All @@ -127,6 +137,6 @@ make check-all # 全栈质量门禁
1. 从最新 `develop` 切 `release/x.y.z`(在该分支 bump 版本与 CHANGELOG)
2. PR:`release/x.y.z` → `main`,合并通过后
3. 在 **main tip** 打并推送 `v<version>`,由 Actions 构建并发布
4. 删除 `release/x.y.z`;必要时再开 `main` → `develop` 同步 PR
4. 删除 `release/x.y.z`;Actions 自动 sync `main` → `develop`(冲突或分支保护时会开 `chore/sync-develop-after-*` PR

Hotfix:从 `main` 拉分支 → 合入 `main`(需发补丁则打 tag)→ 再合入 `develop`。
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<p align="center">
<a href="https://www.python.org/downloads/"><img alt="Python 3.12+" src="https://img.shields.io/badge/python-3.12%2B-blue?logo=python&logoColor=white" /></a>
<a href="https://github.com/TencentCloud/Octop/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green" /></a>
<a href="https://github.com/TencentCloud/Octop/releases"><img alt="Version" src="https://img.shields.io/badge/version-0.9.18-orange" /></a>
<a href="https://github.com/TencentCloud/Octop/releases"><img alt="Version" src="https://img.shields.io/badge/version-0.9.19-orange" /></a>
<a href="https://pypi.org/project/octop/"><img src="https://img.shields.io/pypi/v/octop" alt="PyPI" /></a>
<a href="https://github.com/astral-sh/ruff"><img alt="Code Style: Ruff" src="https://img.shields.io/badge/code%20style-ruff-000000?logo=ruff&logoColor=white" /></a>
<a href="https://github.com/TencentCloud/Octop"><img alt="GitHub stars" src="https://img.shields.io/github/stars/TencentCloud/Octop?style=social" /></a>
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "octop"
version = "0.9.18"
version = "0.9.19"
description = "Smarter self-hosted AI assistant for multiple users and agents, built on harness-agent"
readme = "README.md"
license = { text = "MIT" }
Expand Down
2 changes: 1 addition & 1 deletion src/octop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

from __future__ import annotations

__version__ = "0.9.18"
__version__ = "0.9.19"
Loading
Loading