From 59d25b75797de48628a92a967fec57f717b628ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B1=B1=E5=B7=9D=E5=BF=97?= <3671276527@qq.com>
Date: Mon, 14 Sep 2026 19:08:18 +0800
Subject: [PATCH 1/2] ci: validate learning note links
---
.github/workflows/validate.yml | 2 +-
README.md | 8 ++++++
scripts/validate_notes.py | 45 ++++++++++++++++++++++++++++++++++
3 files changed, 54 insertions(+), 1 deletion(-)
create mode 100644 scripts/validate_notes.py
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index c415be7..cf900ef 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -14,4 +14,4 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Validate note files
- run: python -c "from pathlib import Path; notes=list(Path('.').rglob('*.md')); assert len(notes)>=2 and all(p.read_text(encoding='utf-8').strip() for p in notes)"
+ run: python scripts/validate_notes.py
diff --git a/README.md b/README.md
index 8c55cb0..24c6349 100644
--- a/README.md
+++ b/README.md
@@ -94,6 +94,14 @@ python-notes/
- **GitHub:** [@DorianChn](https://github.com/DorianChn)
- **Email:** 3671276527@qq.com
+## ✅ 本地验证
+
+```bash
+python scripts/validate_notes.py
+```
+
+该检查不需要第三方依赖,会验证笔记非空并检查仓库内的相对链接。
+
---
diff --git a/scripts/validate_notes.py b/scripts/validate_notes.py
new file mode 100644
index 0000000..6b0a1bd
--- /dev/null
+++ b/scripts/validate_notes.py
@@ -0,0 +1,45 @@
+"""Validate the learning-note collection without third-party dependencies."""
+
+from __future__ import annotations
+
+import re
+import sys
+from pathlib import Path
+from urllib.parse import unquote
+
+
+ROOT = Path(__file__).resolve().parents[1]
+LINK = re.compile(r"\[[^\]]+\]\(([^)]+)\)")
+
+
+def fail(message: str) -> None:
+ print(f"validation error: {message}", file=sys.stderr)
+ raise SystemExit(1)
+
+
+def main() -> None:
+ notes = sorted(ROOT.rglob("*.md"))
+ if len(notes) < 2:
+ fail(f"expected at least 2 Markdown files, found {len(notes)}")
+
+ for note in notes:
+ text = note.read_text(encoding="utf-8")
+ if not text.strip():
+ fail(f"empty note: {note.relative_to(ROOT)}")
+ for match in LINK.finditer(text):
+ target = unquote(match.group(1).split("#", 1)[0].strip())
+ if not target or target.startswith(("http://", "https://", "mailto:")):
+ continue
+ candidate = (note.parent / target).resolve()
+ try:
+ candidate.relative_to(ROOT)
+ except ValueError:
+ fail(f"link escapes repository: {note.relative_to(ROOT)} -> {target}")
+ if not candidate.exists():
+ fail(f"missing link: {note.relative_to(ROOT)} -> {target}")
+
+ print(f"validated {len(notes)} Markdown files and all relative links")
+
+
+if __name__ == "__main__":
+ main()
From 2bee75af97ab42e2962a301aa7213bce2d73f829 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B1=B1=E5=B7=9D=E5=BF=97?= <3671276527@qq.com>
Date: Mon, 14 Sep 2026 19:19:29 +0800
Subject: [PATCH 2/2] docs: keep age example current
---
...2\214\346\225\260\346\215\256\347\261\273\345\236\213.md" | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git "a/01-basics/01-\345\217\230\351\207\217\345\222\214\346\225\260\346\215\256\347\261\273\345\236\213.md" "b/01-basics/01-\345\217\230\351\207\217\345\222\214\346\225\260\346\215\256\347\261\273\345\236\213.md"
index b883ea4..f8d38ca 100644
--- "a/01-basics/01-\345\217\230\351\207\217\345\222\214\346\225\260\346\215\256\347\261\273\345\236\213.md"
+++ "b/01-basics/01-\345\217\230\351\207\217\345\222\214\346\225\260\346\215\256\347\261\273\345\236\213.md"
@@ -209,8 +209,9 @@ print("很高兴认识你!")
# 获取出生年份
birth_year = int(input("请输入你的出生年份:"))
-# 计算年龄
-current_year = 2026
+# 使用当前年份计算年龄,避免示例过期
+from datetime import date
+current_year = date.today().year
age = current_year - birth_year
# 输出