8000 chore(release): stop using the changelog by InfinityPacer · Pull Request #58 · InfinityPacer/meterleaf · GitHub
[go: up one dir, main page]

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
1 change: 0 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ Meterleaf 是独立 AI 用量账本,只读采集上游用量并在本地计价
- README 只保留产品定位、核心能力、截图、快速开始和文档入口;使用指南解释操作及结果;部署指南聚焦配置、安装、升级、备份与排障。
- 内部线程、队列、缓存算法等实现细节,除非直接影响操作决策,否则不进入用户文档。确需维护的架构约束单独说明,不把实现复述成产品介绍。
- 改动时重写或合并相关段落,删除重复和过时内容;每条信息保持一个主要出处,用链接引用,不复制多份费率表或配置说明。
- CHANGELOG 按版本记录用户可感知的变化,不复制提交列表。排查过程、机器验收及待办放工作台,不进入公开指南。
- 文档完成后检查命令、默认值、相对链接与截图;截图不包含域名、地址栏、凭据或未经授权的数据。

## 测试要求
Expand Down
17 changes: 0 additions & 17 deletions CHANGELOG.md

This file was deleted.

18 changes: 5 additions & 13 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,15 @@
## 发布流程

1. 更新版本:`bun pm version patch --no-git-tag-version`,新增功能可用 `minor`,也可明确指定版本。
2. 将 `CHANGELOG.md` 未发布内容整理为 `## [版本号] - YYYY-MM-DD`,使用真实发布日期,保留新的未发布区。
3. 运行 `bun test tests`、`bun run build`,提交版本与更新说明,例如 `chore(release): prepare 0.1.1`。
4. 推送到 `main`。Release 工作流自动校验、测试和构建,创建版本标签,将 `linux/amd64`、`linux/arm64` 镜像同时发布到 GHCR 和 Docker Hub,并创建 GitHub Release。
2. 运行 `bun test tests`、`bun run build`,提交版本,例如 `chore(release): prepare 0.1.1`。
3. 推送到 `main`。Release 工作流自动校验、测试和构建,创建版本标签,将 `linux/amd64`、`linux/arm64` 镜像同时发布到 GHCR 和 Docker Hub,并创建 GitHub Release。

## 什么时候发版

合并到 `main` 的提交只要改了 `package.json` 的版本,Release 工作流就会发布一个新版本。所以一件事讨论中的后续修正先留在同一个分支上,规则定下来后一次合并、一次发版,不要每改一轮就升一次版本。只改仓库里的可选脚本或文档、不影响镜像和采集器时,可以不升版本,等下一次正式发版一起写进更新说明。
合并到 `main` 的提交只要改了 `package.json` 的版本,Release 工作流就会发布一个新版本。所以一件事讨论中的后续修正先留在同一个分支上,规则定下来后一次合并、一次发版,不要每改一轮就升一次版本。只改仓库里的可选脚本或文档、不影响镜像和采集器时,可以不升版本,等下一次正式发版一起发布。

## 只保留最新版本

仓库只保留最新的一个版本。新版本发布成功后,删除旧版本的 GitHub Release 和 Git 标签、GHCR 中旧版本的镜像,以及 Docker Hub 上除 `latest` 和最新版本以外的标签;`CHANGELOG.md` 也只保留最新版本的说明。

- GHCR 里没有标签的条目大多是带标签版本实际指向的各架构镜像,只删除不属于保留版本的那些,否则保留的版本也拉不下来。
- 删除前确认 NAS 等部署没有固定使用要删的版本号;已在运行的容器不受影响,但之后重新拉取会失败。

版本未变时跳过自动发布。需要覆盖已有版本时,在 Actions 的 Release 工作流点击「Run workflow」,选择 `main`;这会按该提交重新构建,替换对应 Git 标签、同版本镜像及 Release 说明。
版本未变时跳过自动发布。需要覆盖已有版本时,在 Actions 的 Release 工作流点击「Run workflow」,选择 `main`;这会按该提交重新构建,替换对应 Git 标签和同版本镜像。

首次运行仍需按[部署指南](deployment.md)准备数据目录权限和来源配置。不执行删除数据卷的清理命令。

升级时是否需要先备份,取决于数据库结构版本(`src/storage/schema.ts` 中的 `DATABASE_SCHEMA_REVISION`)有没有变化。没有变化时,旧版本镜像仍能读取升级后的数据,回滚只需换回旧镜像标签,不必为升级单独备份。有变化时,旧版本读不了迁移后的数据库,升级前必须停止服务并备份完整数据目录,更新说明里也要提醒用户先备份。
升级时是否需要先备份,取决于数据库结构版本(`src/storage/schema.ts` 中的 `DATABASE_SCHEMA_REVISION`)有没有变化。没有变化时,旧版本镜像仍能读取升级后的数据,回滚只需换回旧镜像标签,不必为升级单独备份。有变化时,旧版本读不了迁移后的数据库,升级前必须停止服务并备份完整数据目录。
20 changes: 4 additions & 16 deletions scripts/release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@ import { appendFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { version } from "../package.json";

/** 发布标签必须匹配唯一版本源;只接受正式版本,避免预发布覆盖 latest。 */
export function releaseNotes(tag: string, changelog: string): string {
if (!/^\d+\.\d+\.\d+$/.test(version) || tag !== `v${version}`)
throw new Error("发布标签必须与 package.json 的正式版本一致");
const section = changelog
.split(/^## /m)
.find((part) => part.startsWith(`[${version}] - `));
if (!section || !/^\[.*\] - \d{4}-\d{2}-\d{2}\n/.test(section))
throw new Error("CHANGELOG.md 缺少带日期的对应版本");
const notes = section.slice(section.indexOf("\n") + 1).trim();
if (!notes) throw new Error("发布说明不能为空");
return notes;
}

/** 自动运行只发布新版本;手动运行明确允许覆盖已有版本。 */
export function shouldPublish(
currentCommit: string,
Expand All @@ -28,8 +14,9 @@ export function shouldPublish(
}

if (import.meta.main) {
if (!/^\d+\.\d+\.\d+$/.test(version))
throw new Error("发布版本必须是正式版本");
const tag = `v${version}`;
const notes = releaseNotes(tag, await Bun.file("CHANGELOG.md").text());
const repository = process.env.GITHUB_REPOSITORY;
if (!repository || !/^[\w.-]+\/[\w.-]+$/.test(repository))
throw new Error("缺少有效仓库标识");
Expand All @@ -46,7 +33,8 @@ if (import.meta.main) {
tagged.status === 0 ? tagged.stdout.trim() : null,
manual,
);
await Bun.write("release-notes.md", notes + "\n");
// 发布不写说明;工作流的发布步骤仍读取这个文件,留空即可。
await Bun.write("release-notes.md", "");
if (process.env.GITHUB_OUTPUT)
appendFileSync(
process.env.GITHUB_OUTPUT,
Expand Down
20 changes: 1 addition & 19 deletions tests/release.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect, test } from "bun:test";
import { version } from "../package.json";
import { releaseNotes, shouldPublish } from "../scripts/release";
import { shouldPublish } from "../scripts/release";
import {
mkdtemp,
mkdir,
Expand All @@ -13,20 +13,6 @@ import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { spawnSync } from "node:child_process";

test("release requires matching version and dated nonempty notes", () => {
expect(
releaseNotes(
`v${version}`,
`# 更新日志\n## [${version}] - 2026-09-09\n\n- 发布说明\n## [0.0.1] - 2026-01-01\n旧版`,
),
).toBe("- 发布说明");
expect(() => releaseNotes("v99.0.0", "")).toThrow();
expect(() => releaseNotes(`v${version}`, "## [未发布]\n草稿")).toThrow();
expect(() =>
releaseNotes(`v${version}`, `## [${version}] - 2026-09-09\n`),
).toThrow();
});

test("automatic publishing skips existing versions and manual runs allow replacement", () => {
const current = "a".repeat(40);
expect(shouldPublish(current, null)).toBe(true);
Expand Down Expand Up @@ -63,10 +49,6 @@ test("release workflow creates and manually replaces tags using a local-only rem
join(work, "scripts/release.ts"),
);
await writeFile(join(work, "package.json"), JSON.stringify({ version }));
await writeFile(
join(work, "CHANGELOG.md"),
`## [${version}] - 2026-09-09\n\n- Fixture release\n`,
);
command("git", ["init", "--bare", remote]);
command("git", ["init"]);
command("git", ["config", "user.name", "Release Fixture"]);
Expand Down
Loading
0