Skip to content

Refactor/file Add build versioning, environment configuration, and file logging#3

Merged
Embers-of-the-Fire merged 18 commits intomainfrom
refactor/file
Mar 20, 2026
Merged

Refactor/file Add build versioning, environment configuration, and file logging#3
Embers-of-the-Fire merged 18 commits intomainfrom
refactor/file

Conversation

@Embers-of-the-Fire
Copy link
Member

@Embers-of-the-Fire Embers-of-the-Fire commented Mar 18, 2026

本次 PR 在构建/发布、运行时配置、日志记录与数据库初始化等方面进行了重构与增强,提升可部署性、可观测性与多架构发布能力。主要变更如下:

构建与发布

  • CI (.github/workflows/ci.yml):在 Go 构建时通过 -ldflags 注入版本信息(Version = ci-${{ github.sha }}),便于在 CI 环境中追踪二进制版本。
  • Release (.github/workflows/release.yml):
    • 引入 Go 构建矩阵以并行构建 linux/amd64 与 linux/arm64,使用 matrix 驱动的 GOOS/GOARCH。
    • 生成带架构后缀的工件名(如 bot-linux-amd64),并通过 -X 将发布/标签版本嵌入二进制。
    • 为产物生成确定性排序的 SHA256SUMS.txt,并改进 gh release create 时的 artifact 列表生成以保证顺序确定性。

Bot(packages/bot)

  • 新增公开变量 Version(默认 "dev",可通过 -ldflags 覆盖)。
  • 添加命令行标志 -version:打印 Version 后退出(在加载配置前)。
  • 配置加载失败时将错误写入 stderr 并以非零退出码终止。
  • 配置示例(packages/bot/config.example.yaml):默认日志目录由 logs 改为 runtime/logs,存储目录由 storage 改为 runtime/storage。
  • 新增 packages/bot/README.md,记录运行、配置与版本注入/校验(含 SHA256SUMS.txt)说明。

Service(packages/service)

  • 网络与启动
    • 新增环境变量 BIND_ADDR(默认 0.0.0.0:3000),服务监听地址可通过该变量配置;启动时记录实际绑定地址。
    • OpenAPI 文档 server 元数据由绝对 URL 改为相对路径 "/api"。
  • 日志系统
    • 引入基于文件的滚动日志(RollingFileAppender),使用 JSON 编码、按大小(10 MB)轮转、保留最多 5 个归档,同时保留 stdout 输出;日志目录由环境变量驱动。
  • 数据库与模式迁移
    • Database::new 使用 SqliteConnectOptions 并显式启用 SQLite 外键支持。
    • Database::init 新增 network_info 表初始化与迁移逻辑:若表缺失则创建;若存在但缺少外键约束,执行重命名旧表、重建含外键的新表、复制数据并删除旧表的迁移,确保 network_info.robot_uuid 外键指向 robots(uuid) 且 ON DELETE CASCADE。
    • packages/service/schema.sql 为 network_info 添加 FOREIGN KEY (robot_uuid) → robots(uuid) ON DELETE CASCADE。
  • 代码与格式调整
    • 多处日志/格式化字符串改为使用命名/捕获式格式(如 {err}、{robot_id})。
    • 若干 async 函数添加 clippy 抑制属性(unused_async、needless_pass_by_value 等)。
    • 若干内部构造函数/调用点调整了 Result/Ok(...) 的包装以统一返回/错误处理风格(如 Message 及相关指令构造处的同步修改)。

包与构建配置

  • packages/service/Cargo.toml:增加 package 元数据(description、license、repository、publish = false)、profile.release 优化(lto、strip、codegen-units、opt-level)及 clippy lint 配置。

其他维护性改动

  • .gitignore:新增常见编辑器/IDE 与操作系统临时文件规则(*.swp、.idea、.vscode、.DS_Store 等)。
  • 示例/注释与文档更新:包括 packages/service/.env.example(新增 BIND_ADDR)、README 与 docs 文档扩充,多个文件的注释与错误/日志消息格式微调。

总体而言,本次提交围绕版本注入与多架构发布、运行时环境可配置性、结构化与文件化日志、以及数据库外键安全迁移进行了系统性改进,同时对若干内部实现与格式化进行了统一与修整,目标是提升可部署性、可维护性与运行时可观测性。

Embers-of-the-Fire and others added 12 commits March 17, 2026 18:42
The root .gitignore was empty. Add ignore rules for common editor
swap/backup files (vim, Sublime), IDE directories (.idea, .vscode),
and OS-generated artifacts (.DS_Store, Thumbs.db).

Per-package ignores (target/, /runtime, etc.) are already handled by
their respective .gitignore files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add description, license, repository URL, and publish = false to
clarify the package identity and prevent accidental crate publishes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a [profile.release] section with LTO, strip, codegen-units = 1,
and opt-level = 3 to produce smaller, faster release binaries. These
settings are standard for deployment-oriented Rust services.

Add [lints.clippy] section enabling pedantic lints with targeted
allows for module_name_repetitions and must_use_candidate, which
are overly noisy for this web-service codebase.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The runtime init method only created the `robots` table, but
schema.sql also defines `network_info`. This caused
write_network_info / get_network_info queries to fail if the table
did not already exist (e.g. fresh deployments without the CI schema
bootstrapping step).

Mirror the full schema.sql definition so both tables are guaranteed
to exist after Database::init().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the confusing double-error pattern (fmt.Errorf assigned to a
shadowed err, then nil-checked — which is always non-nil) with a
direct fmt.Fprintf to stderr. The error message now goes to stderr
instead of stdout, which is the correct stream for diagnostic output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The service required LOG_DIR and created the directory, but only
logged to stdout console — the env var was effectively unused.

Add a rolling-file appender that writes JSON-encoded log entries to
LOG_DIR/service.log, matching the bot's file-logging behaviour. Logs
rotate when the file exceeds 10 MB, keeping up to 5 gzipped archives.

The default log level is also tightened from Debug to Info for
production readiness; debug output remains reachable by lowering the
filter at runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The server previously hardcoded "0.0.0.0:3000" as the listen address
and "http://localhost:3000/api" as the Swagger server URL.

Introduce a BIND_ADDR environment variable (defaulting to
0.0.0.0:3000) that controls both the TCP listener and the Swagger UI
server URL. This allows deploying on non-standard ports or binding to
a specific interface without code changes.

Also adds a startup log line so operators can confirm which address
the server is listening on.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a package-level Version variable (default "dev") that can be set
at compile time via -ldflags="-X main.Version=...". Also add a
--version CLI flag that prints the version and exits, giving
operators a simple way to verify which build is deployed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI builds now embed "ci-<sha>" as the version, and release builds
embed the git tag (e.g. "v1.2.3"). This pairs with the new --version
flag added to the bot, letting operators verify which exact build is
deployed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Convert the single-arch Go build step into a matrix strategy that
produces both linux/amd64 and linux/arm64 binaries. This supports
deploying the bot on ARM-based SBCs (common in robotics setups).

Add a SHA256SUMS.txt checksums file to the release artifacts so
downstream consumers can verify download integrity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add inline comments explaining each configuration field, set default
paths to runtime/logs and runtime/storage to match the .gitignore
/runtime pattern, and add a copy-to-config.yaml instruction at the
top of the file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Automated formatting fixes from cargo fmt: collapsed single-item
argument lists and shortened import groupings to satisfy the project
max_width = 80 constraint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Embers-of-the-Fire Embers-of-the-Fire self-assigned this Mar 18, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 18, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d2404286-ff26-4148-aec0-baf5fc420596

📥 Commits

Reviewing files that changed from the base of the PR and between 7fd4e5d and 9e2cfa8.

📒 Files selected for processing (3)
  • README.md
  • packages/bot/README.md
  • packages/service/README.md
✅ Files skipped from review due to trivial changes (3)
  • README.md
  • packages/bot/README.md
  • packages/service/README.md

Walkthrough

引入构建时版本注入与多架构发布构建;扩展 .gitignore;Bot 增加版本标志与配置路径更新;Service 增加可配置绑定地址、文件滚动日志、network_info 表及迁移逻辑;若干日志/格式与内部返回类型调整。

Changes

Cohort / File(s) Summary
CI / Release 工作流
.github/workflows/ci.yml, .github/workflows/release.yml
在 Go 编译时注入版本变量(CI 使用 commit SHA,release 使用 tag);release 改为 matrix 构建 linux/amd64 & linux/arm64,产物按平台命名并生成 SHA256SUMS,调整 gh release 调用方式。
忽略与包元数据
.gitignore, packages/service/Cargo.toml
新增常见编辑器/OS 忽略规则;为 service 包添加 description/license/repository/publish=false,并新增 release profile 与 clippy 配置。
Bot:版本与配置
packages/bot/main.go, packages/bot/config.example.yaml
新增公有变量 Version(默认 "dev")并支持 -version 提前打印并退出;config 示例默认日志/存储目录改为 runtime/...;配置加载错误改为写 stderr。
Service:环境常量与示例
packages/service/.env.example, packages/service/src/constant/env.rs
新增 BIND_ADDR 环境项及常量 ENV_NAME_BIND_ADDR / DEFAULT_BIND_ADDR0.0.0.0:3000)。
Service:日志与主流程
packages/service/src/logger.rs, packages/service/src/main.rs
引入文件滚动日志(JSON 编码、大小触发、归档压缩),日志目录由环境驱动;OpenAPI server 地址改为相对 /api,监听地址由 env 决定并记录启动日志。
Service:数据库模式与迁移
packages/service/src/database.rs, packages/service/schema.sql
network_info 添加外键约束指向 robots(uuid)(ON DELETE CASCADE);Database 初始化新增检测/创建 network_info 及在缺 FK 时的迁移流程(重命名旧表、重建带 FK 的表、复制数据、删除旧表、提交事务)。
Service:API / 内部改动汇总
packages/service/src/api*.rs, packages/service/src/service/..., packages/service/src/service/message.rs, packages/service/src/service/instructions/**
大量日志/format 字符串改为命名/捕获格式,添加若干 clippy allow 属性;若干 async closures/constructors改为返回 anyhow::Result<Message> 或把调用处改为传引用;重要签名变更:Message::new_instruction_with_uuidanyhow::Result<Self> 改为直接返回 Self
文档与说明
README.md, packages/bot/README.md, packages/service/README.md, packages/bot/config.example.yaml
更新 README、添加 bot README,文档化版本标志、运行与校验流程;示例配置修改默认 runtime 路径并补充注释。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

兔子的诗

🐰✨
构建里刻着版本号,二进制跨架跑,
日志翻卷写成行,绑定地址找着门,
旧表悄移入新章,小兔鼓掌唱一段。

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/file
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai bot changed the title Refactor/file @coderabbitai Refactor/file Add build versioning, environment configuration, and file logging Mar 18, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
packages/bot/config.example.yaml (1)

6-7: 建议在目录项注释中标明“必填,不能为空”。

结合当前加载逻辑,dir 为空会在启动时触发目录创建错误。建议在 Line 6-7 和 Line 10-11 的注释里明确“必填且不能为空”,让问题前置到配置阶段。

Also applies to: 10-11

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/bot/config.example.yaml` around lines 6 - 7, 在配置示例中针对键名 "dir"
的注释未标明这是必填项且不能为空;请在所有出现 "dir" 的注释(示例中两处描述“Directory for rotating log files
(created
automatically).”的位置)直接追加“必填,不能为空”的说明,以便使用者在配置阶段注意并避免启动时因空路径触发目录创建错误;同时确保注释文本清晰表述“必填且不能为空(required,
non-empty)”以与现有加载逻辑对齐。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/release.yml:
- Around line 84-86: The "Generate SHA256 checksums" step currently runs
"sha256sum *" which fails if artifacts/ contains subdirectories; change the step
so it only enumerates regular files (not directories), sorts them
deterministically, and then computes SHA256 checksums into SHA256SUMS.txt;
update the run command in that step to use a file-only finder + deterministic
sort piped into sha256sum (so SHA256SUMS.txt contains checksums for files only
and in a stable order).

In `@packages/service/.env.example`:
- Line 4: 将 .env.example 中的 BIND_ADDR 键移动到 DATABASE_URL 之前以消除 dotenv-linter 的
UnorderedKey 警告:在文件中找到 BIND_ADDR 和 DATABASE_URL,交换它们的位置(确保 BIND_ADDR 出现在
DATABASE_URL 之上),保存并提交更改以消除 CI/本地 lint 噪音。

In `@packages/service/src/database.rs`:
- Around line 29-39: The network_info table's robot_uuid is only a primary key
and needs a FOREIGN KEY to robots(uuid) to prevent orphan rows and support
cascade deletes: update the CREATE TABLE in the network_info creation (the SQL
executed via sqlx::query! in database.rs) to include "FOREIGN KEY(robot_uuid)
REFERENCES robots(uuid) ON DELETE CASCADE". Also ensure SQLite foreign keys are
enabled when opening the DB by updating the connection initialization (the
new(...) constructor that builds self.connection) to use
sqlx::sqlite::SqliteConnectOptions and set .pragma("foreign_keys","ON") (then
connect_with those options to create the SqlitePool). Finally keep using
.execute(&self.connection).await? for the create statement so the constraint is
applied at startup.

In `@packages/service/src/main.rs`:
- Around line 37-43: The OpenAPI server URL is being built from the listen
address (bind_addr) and passed into OpenApiService::server(), which exposes an
unreachable host; change the code that constructs server_url and the call to
OpenApiService::new(...).server(...) to use the relative path "/api" instead of
format!("http://{bind_addr}/api"). Update the variable or inline value used for
api_service so OpenApiService::server("/api") is used (keep bind_addr only for
the actual listener, not for the OpenAPI spec).

---

Nitpick comments:
In `@packages/bot/config.example.yaml`:
- Around line 6-7: 在配置示例中针对键名 "dir" 的注释未标明这是必填项且不能为空;请在所有出现 "dir"
的注释(示例中两处描述“Directory for rotating log files (created
automatically).”的位置)直接追加“必填,不能为空”的说明,以便使用者在配置阶段注意并避免启动时因空路径触发目录创建错误;同时确保注释文本清晰表述“必填且不能为空(required,
non-empty)”以与现有加载逻辑对齐。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c60f5114-dffe-45b0-a8b8-738df121c4a7

📥 Commits

Reviewing files that changed from the base of the PR and between c75972a and 352981a.

📒 Files selected for processing (11)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • .gitignore
  • packages/bot/config.example.yaml
  • packages/bot/main.go
  • packages/service/.env.example
  • packages/service/Cargo.toml
  • packages/service/src/constant/env.rs
  • packages/service/src/database.rs
  • packages/service/src/logger.rs
  • packages/service/src/main.rs

Embers-of-the-Fire and others added 4 commits March 19, 2026 03:27
Replace the release checksum generation step with a file-only pipeline so
merged artifact downloads do not fail when actions/download-artifact creates
subdirectories. The new implementation walks the artifact tree, excludes the
checksum file itself, and sorts the file list with LC_ALL=C before hashing to
keep SHA256SUMS.txt deterministic across runs.

Update the GitHub release asset upload step to use the same file-only approach.
This prevents directories from being passed to gh release create and keeps the
published asset order stable when the artifact layout changes.

Co-authored-by: Codex <codex@openai.com>
Reorder the service example environment file to follow the key ordering
expected by dotenv-linter. This moves the bind address ahead of the database
URL and keeps the remaining entries in a stable alphabetical sequence.

Keeping the example file ordered removes recurring UnorderedKey noise from CI
and local lint runs, so future configuration changes do not mask real dotenv
issues behind avoidable ordering warnings.

Co-authored-by: Codex <codex@openai.com>
Enable SQLite foreign key enforcement at connection creation time by
switching the pool setup to SqliteConnectOptions::foreign_keys(true). This
ensures every pooled connection enforces the relationship between
network_info.robot_uuid and robots.uuid instead of relying on a one-off PRAGMA
call that would only affect a single connection.

Add the missing foreign key definition to both the runtime schema bootstrap and
schema.sql so new databases and sqlx validation databases share the same table
shape. The relationship now cascades deletes from robots to network_info,
preventing orphaned network rows from surviving robot removal.

Introduce a lightweight init-time migration for existing deployments whose
network_info table predates the foreign key. When the constraint is missing, the
service renames the legacy table, recreates it with the new constraint, copies
existing rows back under foreign key enforcement, and then drops the legacy
copy after a successful transaction.

Co-authored-by: Codex <codex@openai.com>
Stop deriving the OpenAPI server URL from the bind address and publish the
API base as the relative path /api instead. This keeps the generated spec
usable when the service listens on 0.0.0.0 locally and when it is deployed
behind a reverse proxy or TLS terminator.

Using a relative server entry lets Swagger UI and generated clients resolve the
host and scheme from the actual request instead of hard-coding an internal
listener address. That avoids exposing unreachable URLs such as
http://0.0.0.0:3000/api in the published specification.

Co-authored-by: Codex <codex@openai.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/service/src/service/connection.rs (1)

105-105: 建议为 Unknown 分支增加最小告警日志。

显式匹配很好,但完全静默会增加排障成本;建议至少记录一次 warn(不打印 payload 细节,避免潜在敏感信息泄露)。

可选修改示例
-            MessagePayload::Unknown(_) => {}
+            MessagePayload::Unknown(_) => {
+                log::warn!("Ignoring unknown payload for session_id: {session_id}");
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/service/src/service/connection.rs` at line 105, 在对 MessagePayload
进行匹配的分支里,针对 MessagePayload::Unknown(_) 分支增加一次最小级别的告警日志(例如使用 warn 级别),不要打印或格式化
payload 内容以避免泄漏敏感信息;定位到在 connection.rs 中处理消息的匹配(即匹配到 MessagePayload::Unknown(_)
的分支),调用现有的日志记录器(如 connection 的 logger 或模块级 logger)记录一条简短描述性信息(例如 "Received
unknown message payload"),然后保持原有的空实现行为。
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/service/src/api.rs`:
- Around line 25-27: The bad_request function currently maps client errors to
GenericResponse::InternalError (500); update the bad_request(Error) ->
GenericResponse implementation to return GenericResponse::BadRequest instead and
keep the same PlainText format with the error message so client errors produce
an HTTP 400; locate the change in the bad_request function and replace
GenericResponse::InternalError with GenericResponse::BadRequest.

---

Nitpick comments:
In `@packages/service/src/service/connection.rs`:
- Line 105: 在对 MessagePayload 进行匹配的分支里,针对 MessagePayload::Unknown(_)
分支增加一次最小级别的告警日志(例如使用 warn 级别),不要打印或格式化 payload 内容以避免泄漏敏感信息;定位到在 connection.rs
中处理消息的匹配(即匹配到 MessagePayload::Unknown(_) 的分支),调用现有的日志记录器(如 connection 的 logger
或模块级 logger)记录一条简短描述性信息(例如 "Received unknown message payload"),然后保持原有的空实现行为。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: abbf9cbf-a44a-464b-b302-4747dbcaf222

📥 Commits

Reviewing files that changed from the base of the PR and between 1895056 and ee09feb.

📒 Files selected for processing (18)
  • packages/service/src/api.rs
  • packages/service/src/api/action.rs
  • packages/service/src/api/action/update_binary.rs
  • packages/service/src/api/ident.rs
  • packages/service/src/api/stats.rs
  • packages/service/src/database/robot.rs
  • packages/service/src/env.rs
  • packages/service/src/main.rs
  • packages/service/src/service.rs
  • packages/service/src/service/action.rs
  • packages/service/src/service/connection.rs
  • packages/service/src/service/events.rs
  • packages/service/src/service/instructions.rs
  • packages/service/src/service/instructions/fetch_network.rs
  • packages/service/src/service/instructions/sync_robot_name.rs
  • packages/service/src/service/instructions/update_binary.rs
  • packages/service/src/service/instructions/update_metadata.rs
  • packages/service/src/service/message.rs
✅ Files skipped from review due to trivial changes (9)
  • packages/service/src/api/action/update_binary.rs
  • packages/service/src/service/instructions.rs
  • packages/service/src/service.rs
  • packages/service/src/env.rs
  • packages/service/src/main.rs
  • packages/service/src/database/robot.rs
  • packages/service/src/service/action.rs
  • packages/service/src/api/stats.rs
  • packages/service/src/service/events.rs

@Embers-of-the-Fire Embers-of-the-Fire merged commit 732153a into main Mar 20, 2026
4 checks passed
@Embers-of-the-Fire Embers-of-the-Fire deleted the refactor/file branch March 20, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant