Compare commits
3 Commits
eaabb87108
...
f7e4d2f5ea
| Author | SHA1 | Date | |
|---|---|---|---|
| f7e4d2f5ea | |||
| 44c76b48c1 | |||
| 856eeb758c |
@ -110,12 +110,17 @@ grep UseVirtualizationFrameworkRosetta ~/Library/Group\ Containers/group.com.doc
|
|||||||
### 主应用镜像
|
### 主应用镜像
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker buildx build --platform linux/amd64 --load -t localhost/yggdrasil:latest .
|
docker buildx build --platform linux/amd64 --load \
|
||||||
|
--build-arg YGG_BUILD_GIT_DESCRIBE="$(git describe --tags --always --dirty)" \
|
||||||
|
--build-arg YGG_BUILD_GIT_HASH="$(git rev-parse HEAD)" \
|
||||||
|
--build-arg YGG_BUILD_GIT_COMMIT_DATE="$(git log -1 --format=%cd --date=iso-strict)" \
|
||||||
|
-t localhost/yggdrasil:latest .
|
||||||
```
|
```
|
||||||
|
|
||||||
- Dockerfile 用 `dpkg --print-architecture` 检测架构,amd64 腿原生构建 `x86_64-unknown-linux-musl` 静态二进制
|
- Dockerfile 用 `dpkg --print-architecture` 检测架构,amd64 腿原生构建 `x86_64-unknown-linux-musl` 静态二进制
|
||||||
- 首次约 15-30 分钟(Rosetta 下 cargo 全量编译),有 buildkit 缓存后分钟级
|
- 首次约 15-30 分钟(Rosetta 下 cargo 全量编译),有 buildkit 缓存后分钟级
|
||||||
- 产物 `localhost/yggdrasil:latest`,scratch 运行时层约 16MB
|
- 产物 `localhost/yggdrasil:latest`,scratch 运行时层约 16MB
|
||||||
|
- **`--build-arg` 透传 git 信息**:`.dockerignore` 排除 `.git/`,容器内 `build.rs` 跑不了 git,必须在宿主采集后注入,否则镜像的 `x-yggdrasil-git` 响应头恒为 `unknown`。三个 `$(...)` 命令替换在本机 zsh/bash 下执行(非服务器)。`make docker-amd64` 等价(Makefile 已内置同样的采集+透传)
|
||||||
|
|
||||||
### 6 个 Code Runner 沙箱镜像
|
### 6 个 Code Runner 沙箱镜像
|
||||||
|
|
||||||
@ -418,7 +423,11 @@ rm -f /tmp/yggdrasil-*.tar* /tmp/.env
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. 本地重新构建主应用(有缓存,快)
|
# 1. 本地重新构建主应用(有缓存,快)
|
||||||
docker buildx build --platform linux/amd64 --load -t localhost/yggdrasil:latest .
|
docker buildx build --platform linux/amd64 --load \
|
||||||
|
--build-arg YGG_BUILD_GIT_DESCRIBE="$(git describe --tags --always --dirty)" \
|
||||||
|
--build-arg YGG_BUILD_GIT_HASH="$(git rev-parse HEAD)" \
|
||||||
|
--build-arg YGG_BUILD_GIT_COMMIT_DATE="$(git log -1 --format=%cd --date=iso-strict)" \
|
||||||
|
-t localhost/yggdrasil:latest .
|
||||||
# 2. 导出传输
|
# 2. 导出传输
|
||||||
docker save localhost/yggdrasil:latest | gzip > /tmp/yggdrasil-app.tar.gz
|
docker save localhost/yggdrasil:latest | gzip > /tmp/yggdrasil-app.tar.gz
|
||||||
scp /tmp/yggdrasil-app.tar.gz <host>:/root/docker/yggdrasil/
|
scp /tmp/yggdrasil-app.tar.gz <host>:/root/docker/yggdrasil/
|
||||||
|
|||||||
50
CHANGELOG.md
50
CHANGELOG.md
@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.5.0] - 2026-07-22
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **文章数学公式 SSR 渲染(KaTeX)**:引入 `katex-rs` 在服务端把 `$...$`/`$$...$$` TeX 渲染成视觉层 HTML span(`OutputFormat::Html`,不含 MathML,XSS 面最小;`throw_on_error=false` 坏公式渲染成红色错误而非中断);自托管 KaTeX CSS + woff2 字体到 `public/katex/`(`make katex-css` 从 npm `katex` dist 拷贝)。
|
||||||
|
- **评论数学公式渲染**:评论路径同步开启 `ENABLE_MATH`,span 白名单加 `style` 保留 KaTeX 内联定位样式。
|
||||||
|
- **编辑器数学公式节点**:Tiptap 数学公式节点带 KaTeX 预览,根治编辑器序列化破坏 LaTeX 的问题。
|
||||||
|
- **mermaid 流程图懒加载渲染**:文章页 `language-mermaid` 代码块经 IntersectionObserver 视口可见时动态 `import('/mermaid/mermaid.js')`(独立 IIFE bundle,~3.4MB / gzip ~900KB,非全局注入),`mermaid.render` 产 SVG 注入;主题经 `__initMermaid` 传入,`securityLevel: 'strict'`,幂等守卫防重复渲染;渲染失败保留源码并加 `.mermaid-error` class。
|
||||||
|
- **流程图配色对齐 Catppuccin**:mermaid 配色对齐 Catppuccin 主题,容器美化。
|
||||||
|
- **流程图主题切换动画**:流程图主题切换跟随 View Transitions 圆形扩散动画,主题切换时重渲染已渲染的流程图。
|
||||||
|
- **bun 代码运行器**:新增 `yggdrasil-runner-bun` 沙箱镜像(官方 `bun.sh/install` 脚本 + musl 变体 + `libstdc++`/`libgcc` C++ 运行时);admin 代码试运行沙箱加 bun 语言按钮;CodeMirror 加 TypeScript 模式;语言别名归一化(`ts`/`typescript`→`bun`,在 `parse_fence_info` 统一,`LANGUAGES.get` 只见规范化 key)。
|
||||||
|
- **文章页脚注完整支持**:语义化 + back-link + 样式。
|
||||||
|
- **Vue SFC 语法高亮**:文章页代码块支持 Vue SFC 语法高亮。
|
||||||
|
- **搜索入口改为图标按钮**:header 搜索入口从文字改为图标按钮。
|
||||||
|
- **正文折叠块卡片化**:`<details>` 折叠块卡片化,自绘 chevron + hover/focus 态。
|
||||||
|
- **task-list checkbox 自绘**:文章页 task-list checkbox 改用 `appearance:none` 自绘圆角方框。
|
||||||
|
- **代码块字号调整**:文章页代码块字号从 13.6px 调整为 16px。
|
||||||
|
- **响应头暴露版本信息**:server 通过 `Server`/`X-Yggdrasil-Version`/`X-Yggdrasil-Git` 响应头主动暴露版本与 git 描述信息(`EXPOSE_VERSION_HEADERS` 可关)。
|
||||||
|
- **Docker multi-arch 构建目标**:`make docker-amd64` 与 `make docker-apple` 构建 x86_64 镜像。
|
||||||
|
- **服务器端口占用优雅退出**:端口被占用时优雅退出而非 panic。
|
||||||
|
- **压缩算法默认 off**:`COMPRESSION_ALGORITHMS` 中间件默认值改为 off。
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **mimalloc 全局分配器**:用 mimalloc 替换系统全局分配器(`#[global_allocator]`,双 cfg 门控:server feature + 非 wasm32;musl 静态链接友好)。
|
||||||
|
- **性能优化系列**:`escape_html` 链式 5 次 replace 改单遍扫描;`slugify` 单遍状态机重写(分配 4→1);Markdown 渲染消除双解析 + `format!` 改 `write!` 直写;upload 消除 `data.to_vec()` 多次全文件深拷贝;`cache_key` 单次拼接 + `detect_format` 零分配后缀匹配;posts list/search 零 capacity Vec 改 collect 预分配 + helpers retain。
|
||||||
|
- **重构系列**:admin `/admin/posts` 与 `/admin/posts/trash` 合并为单路由 + tab 切换;`Pagination` 支持可选 `on_prev`/`on_next` 回调;抽取 `@yggdrasil/shared` 内部源共享包消除跨 IIFE 库的类型/常量重复;抽 `main.rs` 中间件到 `src/middleware.rs`;为 `Response` 类型添加构造器消除 51 处样板;抽 `invalidate_post_metadata()`/`upload_error()` 等消除样板;统一 WASM sleep 到 `utils::time::sleep_ms`;删除死代码(`delayed_loading.rs`/`ui.rs EmptyState`/`CommentActions`/未用 re-export);拆分 `system.rs` 为 `system/` 目录(按 tab 分文件);图片处理合并维度读取函数共享 `image_reader_limits`。
|
||||||
|
- **依赖升级**:TypeScript 升级至 7.0.2(Go 原生编译器)。
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **SSR 缓存失效根治**:文章写入后物理删除 SSR 磁盘缓存目录,根治「重建后内容不更新」(Dioxus 0.7 增量渲染器只暴露 TTL 失效手段,通过删文件绕过限制);build 前清除 `static/` SSR 缓存目录。
|
||||||
|
- **Docker 构建/部署**:Docker 构建透传 git 信息修复 `x-yggdrasil-git` 恒为 unknown;预装 binaryen 避免 dx 运行时下载 wasm-opt 失败;补齐 Dockerfile 缺失的 katex-css 与 restore-webp 步骤;升级 builder 至 trixie 满足 dx 对 GLIBC_2.39 的需求;用 tmpfs `mode=1777` 替换 uid/gid 选项兼容 Podman;GitHub Releases 下载最终改用直连(移除 gh-proxy)。
|
||||||
|
- **线上代码高亮缺失**:编译期内嵌自定义语法,修复线上 Docker 镜像代码高亮缺失(原先运行时加载语法文件在打包镜像中找不到)。
|
||||||
|
- **mermaid 渲染**:改用 script 标签加载 IIFE bundle 修正全局变量取值;修复 tsc 类型错误;主题切换时重渲染已渲染的流程图。
|
||||||
|
- **文章锚点导航**:`scrollToHash` 增加一次性守卫,切主题不再跳回 URL hash;增加 ResizeObserver 布局稳定期,修正 mermaid 异步渲染导致的锚点落点偏移。
|
||||||
|
- **WASM 双端编译**:hooks 模块移出 server gate 双端可见(原先 WASM 编译失败)。
|
||||||
|
- **评论代码块转义**:统一 `escape_html`,修复代码块单引号未转义。
|
||||||
|
- **Docker daemon 容错**:Docker daemon 不可用/断连时不再 panic(集成测试在无 daemon 环境优雅跳过)。
|
||||||
|
- **后台布局**:重建结果消息改绝对定位,避免撑高容器顶起按钮。
|
||||||
|
- **图片 cfg 门控**:为 `ImageFmt` 别名补上 `#[cfg(feature = "server")]` 门控。
|
||||||
|
- **clippy/lint**:修复 rust-1.97 clippy `useless_borrows_in_formatting` 告警、Biome 告警。
|
||||||
|
- **安全测试**:补齐安全关键路径的单元测试盲区。
|
||||||
|
|
||||||
|
### Internal
|
||||||
|
|
||||||
|
- **skills 体系**:新增 `optimizing-rust-performance` 与 `rust-advanced-performance` 性能优化技能;清理已卸载的第三方 skills 及 lock 注册;`deploy-to-linux` 添加手动部署模式。
|
||||||
|
- **部署脚本**:新增 xun 服务器全量部署脚本。
|
||||||
|
- **文档**:AGENTS.md 补充别名归一化与 bun 镜像说明、数学公式与流程图架构说明、xterm-terminal/shared 库说明;补全 `.env.example` 缺失的 5 个环境变量。
|
||||||
|
|
||||||
## [0.4.0] - 2026-07-13
|
## [0.4.0] - 2026-07-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5591,7 +5591,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yggdrasil"
|
name = "yggdrasil"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"axum",
|
"axum",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "yggdrasil"
|
name = "yggdrasil"
|
||||||
version = "0.4.0"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# 构建时工具二进制:用 syntect(server-only 依赖)生成 highlight.css。
|
# 构建时工具二进制:用 syntect(server-only 依赖)生成 highlight.css。
|
||||||
|
|||||||
18
Dockerfile
18
Dockerfile
@ -148,6 +148,24 @@ COPY libs/lightbox/package.json libs/lightbox/
|
|||||||
COPY libs/yggdrasil-core/package.json libs/yggdrasil-core/
|
COPY libs/yggdrasil-core/package.json libs/yggdrasil-core/
|
||||||
RUN cd libs && pnpm install --frozen-lockfile
|
RUN cd libs && pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# Build-time git info, injected by the caller via --build-arg. `.dockerignore`
|
||||||
|
# excludes `.git/`, so build.rs can't run `git` inside the container — these
|
||||||
|
# ARGs are the only channel for git metadata to reach build.rs (it reads them
|
||||||
|
# via std::env::var, its first-precedence source). Defaults are empty so a
|
||||||
|
# bare `docker build` without args degrades to "unknown" gracefully (build.rs
|
||||||
|
# falls back to the git command, then "unknown").
|
||||||
|
#
|
||||||
|
# Each ARG → ENV pair is needed: ARG is only visible in Dockerfile RUN
|
||||||
|
# commands (and build.rs is invoked by cargo, not a RUN), so we export it as
|
||||||
|
# ENV for the cargo build step to inherit. Both default to empty; Makefile's
|
||||||
|
# docker/docker-amd64/docker-multiarch targets override them on the host.
|
||||||
|
ARG YGG_BUILD_GIT_DESCRIBE=""
|
||||||
|
ARG YGG_BUILD_GIT_HASH=""
|
||||||
|
ARG YGG_BUILD_GIT_COMMIT_DATE=""
|
||||||
|
ENV YGG_BUILD_GIT_DESCRIBE=${YGG_BUILD_GIT_DESCRIBE}
|
||||||
|
ENV YGG_BUILD_GIT_HASH=${YGG_BUILD_GIT_HASH}
|
||||||
|
ENV YGG_BUILD_GIT_COMMIT_DATE=${YGG_BUILD_GIT_COMMIT_DATE}
|
||||||
|
|
||||||
# Copy the rest of the source tree and build everything.
|
# Copy the rest of the source tree and build everything.
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|||||||
19
Makefile
19
Makefile
@ -186,19 +186,32 @@ doc-open:
|
|||||||
# Push examples:
|
# Push examples:
|
||||||
# make docker-multiarch IMAGE=ghcr.io/owner/yggdrasil:latest
|
# make docker-multiarch IMAGE=ghcr.io/owner/yggdrasil:latest
|
||||||
# make docker-multiarch IMAGE=user/yggdrasil:v1 PLATFORMS=linux/amd64
|
# make docker-multiarch IMAGE=user/yggdrasil:v1 PLATFORMS=linux/amd64
|
||||||
|
#
|
||||||
|
# git 信息透传:.dockerignore 排除 .git/,容器内 build.rs 跑不了 git 命令,
|
||||||
|
# 所以在宿主(本 Makefile 里)采集后用 --build-arg 注入。Dockerfile 把 ARG
|
||||||
|
# 再 export 成 ENV,build.rs 的 std::env::var 优先读它。三个值在 Make 变量
|
||||||
|
# 里采集一次,所有 docker target 复用;git 不可用时退化为空串,Dockerfile
|
||||||
|
# 默认值也是空,build.rs 最终降级为 "unknown",不阻断构建。
|
||||||
IMAGE ?= yggdrasil
|
IMAGE ?= yggdrasil
|
||||||
PLATFORMS ?= linux/amd64,linux/arm64
|
PLATFORMS ?= linux/amd64,linux/arm64
|
||||||
|
GIT_DESCRIBE := $(shell git describe --tags --always --dirty 2>/dev/null)
|
||||||
|
GIT_HASH := $(shell git rev-parse HEAD 2>/dev/null)
|
||||||
|
GIT_DATE := $(shell git log -1 --format=%cd --date=iso-strict 2>/dev/null)
|
||||||
|
# build-arg 复用块:每个 docker target 展开一次。空值也传(让 Dockerfile 默认接管)。
|
||||||
|
GIT_BUILD_ARGS = --build-arg YGG_BUILD_GIT_DESCRIBE="$(GIT_DESCRIBE)" \
|
||||||
|
--build-arg YGG_BUILD_GIT_HASH="$(GIT_HASH)" \
|
||||||
|
--build-arg YGG_BUILD_GIT_COMMIT_DATE="$(GIT_DATE)"
|
||||||
docker:
|
docker:
|
||||||
@docker buildx build --load -t yggdrasil .
|
@docker buildx build --load $(GIT_BUILD_ARGS) -t yggdrasil .
|
||||||
|
|
||||||
# Cross-build x86_64 into the local daemon. buildx 仿真非原生架构,无需修改 Dockerfile
|
# Cross-build x86_64 into the local daemon. buildx 仿真非原生架构,无需修改 Dockerfile
|
||||||
# (它本就按 dpkg --print-architecture 自适应选 musl target)。Apple Silicon 上走 QEMU,
|
# (它本就按 dpkg --print-architecture 自适应选 musl target)。Apple Silicon 上走 QEMU,
|
||||||
# 比 native 慢;产物可直接 docker run / docker save 导出。
|
# 比 native 慢;产物可直接 docker run / docker save 导出。
|
||||||
docker-amd64:
|
docker-amd64:
|
||||||
@docker buildx build --platform linux/amd64 --load -t yggdrasil:amd64 .
|
@docker buildx build --platform linux/amd64 --load $(GIT_BUILD_ARGS) -t yggdrasil:amd64 .
|
||||||
|
|
||||||
docker-multiarch:
|
docker-multiarch:
|
||||||
@docker buildx build --platform $(PLATFORMS) -t $(IMAGE) --push .
|
@docker buildx build --platform $(PLATFORMS) $(GIT_BUILD_ARGS) -t $(IMAGE) --push .
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@cargo clean
|
@cargo clean
|
||||||
|
|||||||
38
build.rs
38
build.rs
@ -7,6 +7,9 @@
|
|||||||
//! - git 不可用(非仓库 / tarball 构建)时降级为 "unknown",不 fail the build。
|
//! - git 不可用(非仓库 / tarball 构建)时降级为 "unknown",不 fail the build。
|
||||||
//! - 声明 rerun-if-changed=.git/HEAD,否则 cargo 默认仅在 build.rs 自身变化时
|
//! - 声明 rerun-if-changed=.git/HEAD,否则 cargo 默认仅在 build.rs 自身变化时
|
||||||
//! 重跑,会导致打印的还是旧 hash。
|
//! 重跑,会导致打印的还是旧 hash。
|
||||||
|
//! - 三级取值:编译期环境变量(`YGG_BUILD_*`,CI / docker --build-arg 注入)
|
||||||
|
//! → 本地 `git` 命令 → `"unknown"`。Docker 构建时 `.git/` 不进构建上下文
|
||||||
|
//! (`.dockerignore` 排除),靠 Makefile 在宿主采集后用 `--build-arg` 透传。
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
@ -16,9 +19,9 @@ fn main() {
|
|||||||
// 工作区脏状态变化时 describe 的 --dirty 后缀也会变,重跑一次。
|
// 工作区脏状态变化时 describe 的 --dirty 后缀也会变,重跑一次。
|
||||||
println!("cargo:rerun-if-changed=.git/index");
|
println!("cargo:rerun-if-changed=.git/index");
|
||||||
|
|
||||||
set_env("YGG_BUILD_GIT_DESCRIBE", git_describe());
|
set_env("YGG_BUILD_GIT_DESCRIBE", env_or_git("YGG_BUILD_GIT_DESCRIBE", &["describe", "--tags", "--always", "--dirty"]));
|
||||||
set_env("YGG_BUILD_GIT_HASH", git_hash());
|
set_env("YGG_BUILD_GIT_HASH", env_or_git("YGG_BUILD_GIT_HASH", &["rev-parse", "HEAD"]));
|
||||||
set_env("YGG_BUILD_GIT_COMMIT_DATE", git_commit_date());
|
set_env("YGG_BUILD_GIT_COMMIT_DATE", env_or_git("YGG_BUILD_GIT_COMMIT_DATE", &["log", "-1", "--format=%cd", "--date=iso-strict"]));
|
||||||
set_env("YGG_BUILD_RUSTC_VERSION", rustc_version());
|
set_env("YGG_BUILD_RUSTC_VERSION", rustc_version());
|
||||||
// 编译时刻(Unix 秒)。std 无 ISO 8601 格式化,存秒数由运行时 chrono 解析。
|
// 编译时刻(Unix 秒)。std 无 ISO 8601 格式化,存秒数由运行时 chrono 解析。
|
||||||
set_env("YGG_BUILD_TIME", build_time_unix());
|
set_env("YGG_BUILD_TIME", build_time_unix());
|
||||||
@ -29,20 +32,21 @@ fn set_env(key: &str, value: String) {
|
|||||||
println!("cargo:rustc-env={key}={value}");
|
println!("cargo:rustc-env={key}={value}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `git describe --tags --always --dirty`:最紧凑的"版本 + 提交数 + hash + 脏标记"。
|
/// 三级取值:`YGG_BUILD_<KEY>` 环境变量 → 本地 git 命令 → `"unknown"`。
|
||||||
fn git_describe() -> String {
|
///
|
||||||
git_output(&["describe", "--tags", "--always", "--dirty"]).unwrap_or_else(|| "unknown".into())
|
/// - 第一级优先 `std::env::var`(Docker 构建时由 `--build-arg` → `ARG` → `ENV`
|
||||||
}
|
/// 注入到 build.rs 进程环境),非空即采用——这是 Docker 构建唯一能拿到 git
|
||||||
|
/// 信息的通道(`.git/` 被 `.dockerignore` 排除,容器内没有仓库可查)。
|
||||||
/// 完整 40 位 commit hash。
|
/// - 第二级落回本地 `git` 命令(本地 `cargo build` / 非 Docker CI 走这条)。
|
||||||
fn git_hash() -> String {
|
/// - 兜底 `"unknown"`(tarball 构建、git 未安装等)。
|
||||||
git_output(&["rev-parse", "HEAD"]).unwrap_or_else(|| "unknown".into())
|
fn env_or_git(env_key: &str, git_args: &[&str]) -> String {
|
||||||
}
|
if let Ok(v) = std::env::var(env_key) {
|
||||||
|
let v = v.trim();
|
||||||
/// 提交时间(ISO 8601 strict,带时区偏移,可直接被 chrono 解析)。
|
if !v.is_empty() {
|
||||||
fn git_commit_date() -> String {
|
return v.to_string();
|
||||||
git_output(&["log", "-1", "--format=%cd", "--date=iso-strict"])
|
}
|
||||||
.unwrap_or_else(|| "unknown".into())
|
}
|
||||||
|
git_output(git_args).unwrap_or_else(|| "unknown".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 执行一条 git 命令,返回 trim 后的 stdout。失败返回 None(降级路径)。
|
/// 执行一条 git 命令,返回 trim 后的 stdout。失败返回 None(降级路径)。
|
||||||
|
|||||||
97
scripts/xun.fish
Executable file
97
scripts/xun.fish
Executable file
@ -0,0 +1,97 @@
|
|||||||
|
#!/usr/bin/env fish
|
||||||
|
|
||||||
|
# Yggdrasil 全量部署到 xun(Podman + fish + nginx-proxy 反代)
|
||||||
|
# 主应用 + 5 个 Code Runner 沙箱镜像全量重建、传输、导入、滚动重启
|
||||||
|
# 用法: fish scripts/xun.fish
|
||||||
|
|
||||||
|
# 5 个 runner 子镜像(base 由脚本自己先建,FROM 它)
|
||||||
|
set -l RUNNERS python node go rust bun
|
||||||
|
|
||||||
|
echo "==> [1/7] 构建 runner base 镜像"
|
||||||
|
docker buildx build --platform linux/amd64 --load \
|
||||||
|
-t localhost/yggdrasil-runner-base:latest docker/runner-base; or exit 1
|
||||||
|
docker tag localhost/yggdrasil-runner-base:latest yggdrasil-runner-base:latest; or exit 1
|
||||||
|
|
||||||
|
echo "==> [2/7] 构建主应用镜像(透传 git 信息)"
|
||||||
|
docker buildx build --platform linux/amd64 --load \
|
||||||
|
--build-arg YGG_BUILD_GIT_DESCRIBE=(git describe --tags --always --dirty) \
|
||||||
|
--build-arg YGG_BUILD_GIT_HASH=(git rev-parse HEAD) \
|
||||||
|
--build-arg YGG_BUILD_GIT_COMMIT_DATE=(git log -1 --format=%cd --date=iso-strict) \
|
||||||
|
-t localhost/yggdrasil:latest .; or exit 1
|
||||||
|
|
||||||
|
echo "==> [3/7] 构建 5 个 runner 子镜像"
|
||||||
|
for img in $RUNNERS
|
||||||
|
echo " -- $img"
|
||||||
|
docker buildx build --platform linux/amd64 --load \
|
||||||
|
-t localhost/yggdrasil-runner-$img:latest docker/runner-$img; or exit 1
|
||||||
|
docker tag localhost/yggdrasil-runner-$img:latest yggdrasil-runner-$img:latest; or exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
echo "==> [4/7] 构建验证(期望全 amd64)"
|
||||||
|
set -l ALL_IMAGES yggdrasil yggdrasil-runner-base
|
||||||
|
for img in $RUNNERS
|
||||||
|
set -a ALL_IMAGES yggdrasil-runner-$img
|
||||||
|
end
|
||||||
|
for img in $ALL_IMAGES
|
||||||
|
set -l arch (docker image inspect localhost/$img:latest --format "{{.Architecture}}")
|
||||||
|
echo " $img: $arch"
|
||||||
|
if test "$arch" != "amd64"
|
||||||
|
echo " 架构错误!期望 amd64" >&2
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
echo "==> [5/7] 导出 + 传输到 xun"
|
||||||
|
# 主应用单独一个 tar(滚动重启时只 reload 它)
|
||||||
|
docker save localhost/yggdrasil:latest -o /tmp/yggdrasil-app.tar; or exit 1
|
||||||
|
# 6 个 runner 镜像打包成一个 tar(base + 5 子镜像)
|
||||||
|
docker save \
|
||||||
|
localhost/yggdrasil-runner-base:latest \
|
||||||
|
localhost/yggdrasil-runner-python:latest \
|
||||||
|
localhost/yggdrasil-runner-node:latest \
|
||||||
|
localhost/yggdrasil-runner-go:latest \
|
||||||
|
localhost/yggdrasil-runner-rust:latest \
|
||||||
|
localhost/yggdrasil-runner-bun:latest \
|
||||||
|
-o /tmp/yggdrasil-runners.tar; or exit 1
|
||||||
|
gzip -f /tmp/yggdrasil-app.tar /tmp/yggdrasil-runners.tar; or exit 1
|
||||||
|
scp /tmp/yggdrasil-app.tar.gz /tmp/yggdrasil-runners.tar.gz xun:/root/docker/yggdrasil/; or exit 1
|
||||||
|
rm -f /tmp/yggdrasil-app.tar.gz /tmp/yggdrasil-runners.tar.gz
|
||||||
|
|
||||||
|
echo "==> [6/7] 服务器导入 + runner 去前缀 + 滚动重启 app"
|
||||||
|
ssh xun 'cd /root/docker/yggdrasil && gunzip -kf yggdrasil-app.tar.gz && gunzip -kf yggdrasil-runners.tar.gz'; or exit 1
|
||||||
|
ssh xun 'docker load -i /root/docker/yggdrasil/yggdrasil-app.tar'; or exit 1
|
||||||
|
ssh xun 'docker load -i /root/docker/yggdrasil/yggdrasil-runners.tar'; or exit 1
|
||||||
|
# runner 去 localhost/ 前缀:LANGUAGES 注册表硬编码 yggdrasil-runner-*:latest(无前缀、无 env 覆盖)
|
||||||
|
# fish 不认 bash for 循环,逐条 ssh 执行
|
||||||
|
ssh xun 'docker tag localhost/yggdrasil-runner-base:latest yggdrasil-runner-base:latest'
|
||||||
|
ssh xun 'docker tag localhost/yggdrasil-runner-python:latest yggdrasil-runner-python:latest'
|
||||||
|
ssh xun 'docker tag localhost/yggdrasil-runner-node:latest yggdrasil-runner-node:latest'
|
||||||
|
ssh xun 'docker tag localhost/yggdrasil-runner-go:latest yggdrasil-runner-go:latest'
|
||||||
|
ssh xun 'docker tag localhost/yggdrasil-runner-rust:latest yggdrasil-runner-rust:latest'
|
||||||
|
ssh xun 'docker tag localhost/yggdrasil-runner-bun:latest yggdrasil-runner-bun:latest'
|
||||||
|
# 滚动重启:只重建 app 容器(postgres 和数据卷不动)
|
||||||
|
ssh xun 'cd /root/docker/yggdrasil && docker compose --env-file .env up -d app'; or exit 1
|
||||||
|
ssh xun 'rm -f /root/docker/yggdrasil/yggdrasil-app.tar* /root/docker/yggdrasil/yggdrasil-runners.tar*'
|
||||||
|
|
||||||
|
echo "==> [7/7] 验证"
|
||||||
|
echo "--- 容器状态(期望 postgres healthy + app up)---"
|
||||||
|
ssh xun 'docker ps --filter name=yggdrasil --format "{{.Names}} {{.Status}}"'
|
||||||
|
echo "--- 迁移日志(期望 applied,无 error/panic)---"
|
||||||
|
ssh xun 'docker logs yggdrasil-app 2>&1 | grep -iE "migrat|error|panic" | tail'
|
||||||
|
echo "--- runner 镜像(期望全 found amd64)---"
|
||||||
|
ssh xun 'docker inspect yggdrasil-runner-base:latest --format "base: found {{.Architecture}}"'
|
||||||
|
ssh xun 'docker inspect yggdrasil-runner-python:latest --format "python: found {{.Architecture}}"'
|
||||||
|
ssh xun 'docker inspect yggdrasil-runner-node:latest --format "node: found {{.Architecture}}"'
|
||||||
|
ssh xun 'docker inspect yggdrasil-runner-go:latest --format "go: found {{.Architecture}}"'
|
||||||
|
ssh xun 'docker inspect yggdrasil-runner-rust:latest --format "rust: found {{.Architecture}}"'
|
||||||
|
ssh xun 'docker inspect yggdrasil-runner-bun:latest --format "bun: found {{.Architecture}}"'
|
||||||
|
echo "--- 健康检查 ---"
|
||||||
|
ssh xun 'docker exec nginx-proxy curl -s http://yggdrasil-app:3000/healthz'; echo
|
||||||
|
ssh xun 'docker exec nginx-proxy curl -s http://yggdrasil-app:3000/readyz'; echo
|
||||||
|
echo "--- git 版本头(确认不再是 unknown)---"
|
||||||
|
ssh xun 'docker exec nginx-proxy curl -sI http://yggdrasil-app:3000/ | grep -i x-yggdrasil-git'
|
||||||
|
echo "--- 外部 HTTPS ---"
|
||||||
|
curl -s https://rua.plus/healthz; echo
|
||||||
|
curl -sI https://rua.plus/ | grep -iE "HTTP/|strict-transport"
|
||||||
|
|
||||||
|
echo "==> 部署完成"
|
||||||
Loading…
x
Reference in New Issue
Block a user