Skip to content

Commit 36ac1d5

Browse files
committed
feat: refine instance UI and docker versioning
1 parent 70a1158 commit 36ac1d5

23 files changed

Lines changed: 610 additions & 156 deletions

File tree

.github/workflows/docker.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ jobs:
2525
outputs:
2626
version: ${{ steps.meta.outputs.version }}
2727
image_tag: ${{ steps.image_tag.outputs.TAG }}
28+
app_version: ${{ steps.app_version.outputs.VERSION }}
2829
steps:
2930
- name: Checkout
3031
uses: actions/checkout@v5
@@ -49,13 +50,19 @@ jobs:
4950
username: ${{ env.DOCKERHUB_USERNAME }}
5051
password: ${{ env.DOCKERHUB_TOKEN }}
5152

53+
- name: Read package version
54+
id: app_version
55+
run: |
56+
VERSION="$(awk -F'\"' '/^version = \"/ {print $2; exit}' pyproject.toml)"
57+
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"
58+
5259
- name: Generate image tag
5360
id: image_tag
5461
run: |
5562
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
5663
TAG="${GITHUB_REF#refs/tags/v}"
5764
else
58-
TAG="${GITHUB_SHA::7}"
65+
TAG="${GITHUB_REF_NAME}"
5966
fi
6067
echo "TAG=$TAG" >> "$GITHUB_OUTPUT"
6168
@@ -70,7 +77,6 @@ jobs:
7077
type=ref,event=branch
7178
type=semver,pattern={{version}}
7279
type=semver,pattern={{major}}.{{minor}}
73-
type=sha,prefix=
7480
type=raw,value=latest,enable={{is_default_branch}}
7581
7682
- name: Build and Push
@@ -80,7 +86,8 @@ jobs:
8086
platforms: linux/amd64
8187
push: true
8288
build-args: |
83-
SOURCE_COMMIT=${{ steps.image_tag.outputs.TAG }}
89+
APP_VERSION=${{ steps.app_version.outputs.VERSION }}
90+
VCS_REF=${{ github.sha }}
8491
PYTHON_IMAGE=3.11
8592
VARIANT=-slim
8693
tags: ${{ steps.meta.outputs.tags }}

Dockerfile

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
ARG SOURCE_COMMIT=
1+
ARG APP_VERSION=0.4.2
2+
ARG VCS_REF=
23
ARG PYTHON_IMAGE=3.11
34
ARG VARIANT=
45
ARG APT_MIRROR=
@@ -21,6 +22,8 @@ RUN pnpm -C frontend run build-only
2122

2223
FROM python:${PYTHON_IMAGE}${VARIANT:+-$VARIANT} AS build-stage
2324

25+
ARG APP_VERSION=0.4.2
26+
ARG VCS_REF=unknown
2427
ARG PIP_INDEX_URL
2528
ARG PIP_EXTRA_INDEX_URL
2629
ARG PIP_TRUSTED_HOST
@@ -48,18 +51,29 @@ RUN pip install --no-deps .
4851

4952
FROM python:${PYTHON_IMAGE}${VARIANT:+-$VARIANT}
5053

54+
ARG APP_VERSION=0.4.2
55+
ARG VCS_REF=unknown
5156
ARG APT_MIRROR
5257

5358
EXPOSE 18080
5459

55-
ENV WEBUI_BUILD=${SOURCE_COMMIT}
60+
LABEL org.opencontainers.image.version="${APP_VERSION}"
61+
LABEL org.opencontainers.image.revision="${VCS_REF}"
62+
63+
ENV WEBUI_BUILD=1
64+
ENV WEBUI_VERSION=${APP_VERSION}
65+
ENV WEBUI_VCS_REF=${VCS_REF}
5666
ENV DEBIAN_FRONTEND=noninteractive
5767

5868
COPY --from=build-stage /usr/local /usr/local
5969
COPY --from=build-stage /app /app
6070

6171
WORKDIR /app
6272

73+
RUN mkdir -p /usr/local/share/nb-cli-plugin-webui \
74+
&& printf '%s' "${APP_VERSION}" > /usr/local/share/nb-cli-plugin-webui/version \
75+
&& printf '%s' "${VCS_REF}" > /usr/local/share/nb-cli-plugin-webui/revision
76+
6377
RUN if [ -n "$APT_MIRROR" ]; then \
6478
mirror="${APT_MIRROR%/}"; \
6579
for f in /etc/apt/sources.list /etc/apt/sources.list.d/debian.sources /etc/apt/sources.list.d/debian.list; do \
@@ -91,7 +105,27 @@ RUN if [ -n "$APT_MIRROR" ]; then \
91105
libxcursor1 \
92106
libgbm1 \
93107
libgtk-3-0 \
108+
libgtk-4-1 \
109+
libgstreamer1.0-0 \
110+
libgstreamer-plugins-base1.0-0 \
111+
libgstreamer-gl1.0-0 \
112+
libgstreamer-plugins-bad1.0-0 \
113+
libgraphene-1.0-0 \
114+
libvulkan1 \
115+
libwoff1 \
116+
libvpx9 \
117+
libopus0 \
118+
libflite1 \
119+
libjxl0.11 \
120+
libavif16 \
121+
libenchant-2-2 \
122+
libsecret-1-0 \
123+
libhyphen0 \
124+
libmanette-0.2-0 \
125+
libgles2 \
126+
libx264-164 \
94127
libasound2t64 \
128+
&& ln -sf /usr/lib/x86_64-linux-gnu/libx264.so.164 /usr/lib/x86_64-linux-gnu/libx264.so \
95129
&& rm -rf /var/lib/apt/lists/*
96130

97131
CMD nb ui run

README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,20 +152,23 @@ docker pull docker.io/xisoul/nonebot-webui:latest
152152
Docker 镜像可以选择以下版本:
153153

154154
- `latest`: 默认分支最新可用镜像
155-
- `master`: 默认分支镜像
156-
- `${commit_sha7}`: 指定 commit 的短 SHA 镜像
157-
- `${version}` / `${major}.${minor}`: 当推送 `v*` tag 时自动生成的版本镜像
155+
- `master`: 默认分支镜像,适合持续跟进测试
156+
- `${version}` / `${major}.${minor}`: 当推送 `v*` tag 时自动生成的正式版本镜像,例如 `0.4.2``0.4`
157+
158+
当前不再推荐使用 GitHub commit 短 SHA 作为公开版本标签。镜像版本统一以显式版本号为主,便于后续做镜像更新检测、版本对比和回滚。
158159

159160
例如:
160161

161162
```shell
162163
docker pull ghcr.io/xisoul/nonebot-webui:latest
163164
docker pull ghcr.io/xisoul/nonebot-webui:master
164-
docker pull ghcr.io/xisoul/nonebot-webui:<commit_sha7>
165+
docker pull ghcr.io/xisoul/nonebot-webui:0.4.2
166+
docker pull ghcr.io/xisoul/nonebot-webui:0.4
165167

166168
docker pull docker.io/xisoul/nonebot-webui:latest
167169
docker pull docker.io/xisoul/nonebot-webui:master
168-
docker pull docker.io/xisoul/nonebot-webui:<commit_sha7>
170+
docker pull docker.io/xisoul/nonebot-webui:0.4.2
171+
docker pull docker.io/xisoul/nonebot-webui:0.4
169172
```
170173

171174
### 生产部署
@@ -208,6 +211,16 @@ Docker 运行模式下项目默认根目录为 `/projects`。
208211
./deploy.sh
209212
```
210213

214+
默认情况下,这两个脚本都会自动读取 `pyproject.toml` 中的版本号作为 Docker tag。
215+
如果你想手动指定镜像版本,也可以直接传参:
216+
217+
```shell
218+
./build-and-export.sh 0.4.2
219+
./deploy.sh 0.4.2
220+
```
221+
222+
容器内显示的 WebUI 版本也会优先使用这个显式版本号,而不是 GitHub 随机 commit 字符串。
223+
211224
本镜像当前不内置 Playwright Linux 系统依赖;若你管理的外部项目自身依赖 Playwright,请在对应项目运行环境中单独安装。
212225

213226
### 登录说明

build-and-export.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
set -euo pipefail
44

55
IMAGE_NAME="nonebot-webui"
6-
TAG="${1:-$(git rev-parse --short HEAD 2>/dev/null || echo unknown)}"
6+
DEFAULT_TAG="$(awk -F'\"' '/^version = \"/ {print $2; exit}' pyproject.toml)"
7+
TAG="${1:-${DEFAULT_TAG:-latest}}"
8+
VCS_REF="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
79
OUTPUT_DIR="${OUTPUT_DIR:-./dist-images}"
810
ARCHIVE_NAME="${IMAGE_NAME}-${TAG}.tar"
911
ARCHIVE_PATH="${OUTPUT_DIR}/${ARCHIVE_NAME}"
@@ -19,7 +21,8 @@ echo "[1/3] 构建 Docker 镜像..."
1921
docker build \
2022
-t "${IMAGE_NAME}:${TAG}" \
2123
-t "${IMAGE_NAME}:latest" \
22-
--build-arg SOURCE_COMMIT="${TAG}" \
24+
--build-arg APP_VERSION="${TAG}" \
25+
--build-arg VCS_REF="${VCS_REF}" \
2326
--build-arg PYTHON_IMAGE=3.11 \
2427
--build-arg VARIANT=-slim \
2528
--build-arg APT_MIRROR="${APT_MIRROR:-${WEBUI_DEBIAN_MIRROR:-}}" \

deploy.sh

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# deploy.sh - 本地构建 Docker 镜像并部署到测试服务器
44
#
55
# 用法:
6-
# ./deploy.sh # 使用 git short hash 作为 tag
7-
# ./deploy.sh v0.4.2 # 使用指定 tag
6+
# ./deploy.sh # 使用 pyproject.toml 里的版本号作为 tag
7+
# ./deploy.sh 0.4.2 # 使用指定版本号
88
#
99
set -euo pipefail
1010

@@ -18,8 +18,9 @@ CONTAINER_NAME="nonebot-webui"
1818
if [ -n "${1:-}" ]; then
1919
TAG="$1"
2020
else
21-
TAG=$(git rev-parse --short HEAD)
21+
TAG="$(awk -F'\"' '/^version = \"/ {print $2; exit}' pyproject.toml)"
2222
fi
23+
VCS_REF="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
2324

2425
FULL_IMAGE="${IMAGE_NAME}:${TAG}"
2526

@@ -33,7 +34,8 @@ echo "========================================"
3334
echo ""
3435
echo "[1/4] Building Docker image..."
3536
docker build \
36-
--build-arg SOURCE_COMMIT="$TAG" \
37+
--build-arg APP_VERSION="$TAG" \
38+
--build-arg VCS_REF="$VCS_REF" \
3739
--build-arg PYTHON_IMAGE=3.11 \
3840
--build-arg VARIANT=-slim \
3941
--build-arg APT_MIRROR="${APT_MIRROR:-${WEBUI_DEBIAN_MIRROR:-}}" \

frontend/src/client/api/schemas.gen.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,13 @@ export const NoneBotProjectMetaSchema = {
684684
type: 'string'
685685
}
686686
},
687+
discovered_plugin_dirs: {
688+
title: 'Discovered Plugin Dirs',
689+
type: 'array',
690+
items: {
691+
type: 'string'
692+
}
693+
},
687694
builtin_plugins: {
688695
title: 'Builtin Plugins',
689696
type: 'array',
@@ -756,7 +763,14 @@ export const ProcessLogSchema = {
756763

757764
export const ProjectTomlDetailSchema = {
758765
title: 'ProjectTomlDetail',
759-
required: ['project_name', 'adapters', 'plugins', 'plugin_dirs', 'builtin_plugins'],
766+
required: [
767+
'project_name',
768+
'adapters',
769+
'plugins',
770+
'plugin_dirs',
771+
'discovered_plugin_dirs',
772+
'builtin_plugins'
773+
],
760774
type: 'object',
761775
properties: {
762776
project_name: {
@@ -787,6 +801,13 @@ export const ProjectTomlDetailSchema = {
787801
type: 'string'
788802
}
789803
},
804+
discovered_plugin_dirs: {
805+
title: 'Discovered Plugin Dirs',
806+
type: 'array',
807+
items: {
808+
type: 'string'
809+
}
810+
},
790811
builtin_plugins: {
791812
title: 'Builtin Plugins',
792813
type: 'array',

frontend/src/client/api/types.gen.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ export type NoneBotProjectMeta = {
184184
drivers: Array<nb_cli_plugin_webui__app__models__base__ModuleInfo>
185185
plugins: Array<nb_cli_plugin_webui__app__models__base__Plugin>
186186
plugin_dirs: Array<string>
187+
discovered_plugin_dirs?: Array<string>
187188
builtin_plugins: Array<string>
188189
is_running?: boolean
189190
use_env?: string
@@ -210,6 +211,7 @@ export type ProjectTomlDetail = {
210211
}>
211212
plugins: Array<string>
212213
plugin_dirs: Array<string>
214+
discovered_plugin_dirs: Array<string>
213215
builtin_plugins: Array<string>
214216
}
215217

frontend/src/components/Modals/AddBot/GetBotBasicInfo.vue

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
<script setup lang="ts">
22
import { ProjectService } from '@/client/api'
3-
import { ref } from 'vue'
3+
import { computed, ref } from 'vue'
44
import { useAddBotStore } from '.'
55
66
const store = useAddBotStore()
77
88
const inputValue = ref('')
9+
const discoveredOnlyPluginDirs = computed(() =>
10+
store.discoveredPluginDirs.filter((dir) => !store.pluginDirs.includes(dir))
11+
)
912
1013
const search = async () => {
1114
if (!inputValue.value) {
@@ -28,6 +31,7 @@ const search = async () => {
2831
store.adapters = detail.adapters
2932
store.plugins = detail.plugins
3033
store.pluginDirs = detail.plugin_dirs
34+
store.discoveredPluginDirs = detail.discovered_plugin_dirs ?? []
3135
store.builtinPlugins = detail.builtin_plugins
3236
store.projectPath = inputValue.value
3337
@@ -96,7 +100,7 @@ const search = async () => {
96100
}"
97101
>
98102
<span class="font-semibold">
99-
{{ store.plugins.length ? '已有插件:' : '未找到插件' }}
103+
{{ store.plugins.length ? '已配置插件:' : '未配置插件' }}
100104
</span>
101105
<div class="flex items-center flex-wrap gap-2">
102106
<span
@@ -108,11 +112,16 @@ const search = async () => {
108112
{{ plugin }}
109113
</span>
110114
</div>
115+
<span v-if="!store.plugins.length" class="text-sm opacity-70">
116+
这里显示的是 <span class="font-mono">tool.nonebot.plugins</span> 里声明的插件;
117+
大多数通过 pip 安装的插件本体都在项目的
118+
<span class="font-mono">.venv/site-packages</span> 中,而不是本地插件目录。
119+
</span>
111120
</div>
112121

113122
<div class="flex flex-col gap-2 rounded-lg p-4 bg-base-200">
114123
<span class="font-semibold">
115-
{{ store.pluginDirs.length ? '已有插件目录:' : '未配置插件目录' }}
124+
{{ store.pluginDirs.length ? '已声明本地插件目录:' : '未声明本地插件目录' }}
116125
</span>
117126
<div v-if="store.pluginDirs.length" class="flex items-center flex-wrap gap-2">
118127
<span
@@ -125,8 +134,33 @@ const search = async () => {
125134
</span>
126135
</div>
127136
<span v-else class="text-sm opacity-70">
128-
当前项目没有在配置里声明 <span class="font-mono">plugin_dirs</span>,这不影响继续导入;
129-
已安装插件仍会按项目配置读取。
137+
当前项目没有在 <span class="font-mono">tool.nonebot.plugin_dirs</span> 中声明本地插件目录。
138+
这不影响继续导入,也不代表项目没有安装插件。
139+
</span>
140+
</div>
141+
142+
<div
143+
:class="{
144+
'flex flex-col gap-2 rounded-lg p-4 bg-base-200': true,
145+
'opacity-50': !discoveredOnlyPluginDirs.length
146+
}"
147+
>
148+
<span class="font-semibold">
149+
{{ discoveredOnlyPluginDirs.length ? '扫描到的候选本地插件目录:' : '未扫描到额外候选目录' }}
150+
</span>
151+
<div v-if="discoveredOnlyPluginDirs.length" class="flex items-center flex-wrap gap-2">
152+
<span
153+
v-for="plugin_dir in discoveredOnlyPluginDirs"
154+
:key="plugin_dir"
155+
role="button"
156+
class="badge badge-lg !bg-base-100"
157+
>
158+
{{ plugin_dir }}
159+
</span>
160+
</div>
161+
<span v-if="discoveredOnlyPluginDirs.length" class="text-sm opacity-70">
162+
这些目录是根据常见目录结构自动识别出来的,仅供你核对;
163+
它们当前并不等于项目已经声明的 <span class="font-mono">plugin_dirs</span> 配置。
130164
</span>
131165
</div>
132166
</div>

0 commit comments

Comments
 (0)