diff --git a/UPDATELOG.md b/UPDATELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..6083172c4ae15e5e0d9cc34d743dd3d93ced5873
--- /dev/null
+++ b/UPDATELOG.md
@@ -0,0 +1,11 @@
+## v0.0.23
+
+### Features
+
+- i18n supports
+- Remote profile User Agent supports
+
+### Bug Fixes
+
+- clash config file case ignore
+- clash `external-controller` only port
diff --git a/scripts/publish.mjs b/scripts/publish.mjs
index 6c2e471a5b24a57343dcd248bf25cf0c30f212af..523ad18c3e25454919bc0e9987c40a1e59842fda 100644
--- a/scripts/publish.mjs
+++ b/scripts/publish.mjs
@@ -1,9 +1,11 @@
 import fs from "fs-extra";
 import { createRequire } from "module";
 import { execSync } from "child_process";
+import { resolveUpdateLog } from "./updatelog.mjs";
 
 const require = createRequire(import.meta.url);
 
+// publish
 async function resolvePublish() {
   const flag = process.argv[2] ?? "patch";
   const packageJson = require("../package.json");
@@ -26,6 +28,10 @@ async function resolvePublish() {
   packageJson.version = nextVersion;
   tauriJson.package.version = nextVersion;
 
+  // 发布更新前先写更新日志
+  const nextTag = `v${nextVersion}`;
+  await resolveUpdateLog(nextTag);
+
   await fs.writeFile(
     "./package.json",
     JSON.stringify(packageJson, undefined, 2)
diff --git a/scripts/release.mjs b/scripts/release.mjs
index f2ffa21706375fb3119375cda13437cf0a8186be..8d12c32bbf6ce09af80afec98256b78bc6b38252 100644
--- a/scripts/release.mjs
+++ b/scripts/release.mjs
@@ -1,5 +1,6 @@
 import fetch from "node-fetch";
 import { getOctokit, context } from "@actions/github";
+import { resolveUpdateLog } from "./updatelog.mjs";
 
 const UPDATE_TAG_NAME = "updater";
 const UPDATE_JSON_FILE = "update.json";
@@ -34,7 +35,7 @@ async function resolveRelease() {
 
   const updateData = {
     name: tag.name,
-    notes: latestRelease.body, // use the release body directly
+    notes: await resolveUpdateLog(tag.name), // use updatelog.md
     pub_date: new Date().toISOString(),
     platforms: {
       win64: { signature: "", url: "" },
diff --git a/scripts/updatelog.mjs b/scripts/updatelog.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..fae7f628f42e13512b9bfdff921b58c936a9ae2b
--- /dev/null
+++ b/scripts/updatelog.mjs
@@ -0,0 +1,44 @@
+import fs from "fs-extra";
+import path from "path";
+
+const UPDATE_LOG = "UPDATELOG.md";
+
+// parse the UPDATELOG.md
+export async function resolveUpdateLog(tag) {
+  const cwd = process.cwd();
+
+  const reTitle = /^## v[\d\.]+/;
+  const reEnd = /^---/;
+
+  const file = path.join(cwd, UPDATE_LOG);
+
+  if (!(await fs.pathExists(file))) {
+    throw new Error("could not found UPDATELOG.md");
+  }
+
+  const data = await fs.readFile(file).then((d) => d.toString("utf8"));
+
+  const map = {};
+  let p = "";
+
+  data.split("\n").forEach((line) => {
+    if (reTitle.test(line)) {
+      p = line.slice(3).trim();
+      if (!map[p]) {
+        map[p] = [];
+      } else {
+        throw new Error(`Tag ${p} dup`);
+      }
+    } else if (reEnd.test(line)) {
+      p = "";
+    } else if (p) {
+      map[p].push(line);
+    }
+  });
+
+  if (!map[tag]) {
+    throw new Error(`could not found "${tag}" in UPDATELOG.md`);
+  }
+
+  return map[tag].join("\n").trim();
+}