From d836356a6d791fd3b96fb84def7d035e565e3949 Mon Sep 17 00:00:00 2001 From: halfrost Date: Sun, 21 Jun 2026 18:48:44 -0700 Subject: [PATCH] Update badge --- .github/workflows/deploy.yml | 37 +++++++++ README.md | 17 ++-- .../content/ChapterFour/pytool/WordCount.py | 79 +++++++++++++------ 3 files changed, 101 insertions(+), 32 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c4c3cd873..892d8582f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -51,3 +51,40 @@ jobs: -e "ssh -i ~/.ssh/deploy_key -p ${SERVER_PORT:-22} -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o PubkeyAcceptedAlgorithms=+ssh-rsa" \ ./website/public/ \ "${SSH_USERNAME}@${SSH_HOST}:${SERVER_DESTINATION:-/var/www/books/leetcode/}" + + word-count: # 重新统计总字数并把 README 里的徽章数字刷新、自动提交回仓库 + # 只在 master 上跑(add_hugo 等分支不自动改 README)。 + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + permissions: + contents: write # 需要写权限才能 commit & push 回仓库 + steps: + - uses: actions/checkout@v5 + + - name: Compute total word count # 跑仓库自带脚本(标准库即可,ubuntu-latest 自带 python3) + id: wc + run: | + COUNT=$(python3 website/content/ChapterFour/pytool/WordCount.py | awk '/total word count/{print $NF}') + # 防御:脚本异常时不要用空值/非数字去覆盖徽章 + if ! echo "$COUNT" | grep -qE '^[0-9]+$'; then + echo "WordCount.py 未返回有效数字:'$COUNT'"; exit 1 + fi + echo "total word count = $COUNT" + echo "count=$COUNT" >> "$GITHUB_OUTPUT" + + - name: Update badge & commit if changed + run: | + # 只替换 README 里 Total Word Count 徽章的数字部分 + COUNT="${{ steps.wc.outputs.count }}" + sed -i -E "s#(Total%20Word%20Count-)[0-9]+(-success)#\1${COUNT}\2#" README.md + if git diff --quiet -- README.md; then + echo "徽章已是最新(${COUNT}),无需提交。" + else + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add README.md + # [skip ci]:这次只改 README 的自动提交不必再触发任何 workflow(deploy 已 paths-ignore, + # 但 test.yml 没有,所以统一用 skip ci 防止多余构建,也彻底杜绝循环触发)。 + git commit -m "chore: update Total Word Count badge to ${COUNT} [skip ci]" + git push + fi diff --git a/README.md b/README.md index e4fb87d31..3a6ac14d1 100755 --- a/README.md +++ b/README.md @@ -10,28 +10,27 @@ ![](./website/static/wechat-qr-code.png)

-GitHub All Releases - +GitHub All Releases + Deploy Status Go Test Status - -Support Go version +Go Version

-GitHub - +Code License +Book License - +

@@ -3090,6 +3089,10 @@ Thank you for reading here. This is bonus. You can download my [《ACM-ICPC Algo +## License + +The **code** in this repository (all Go solutions) is licensed under the [MIT License](https://github.com/halfrost/LeetCode-Go/blob/master/LICENSE). The **book content** of *LeetCode Cookbook* (the articles and illustrations served from the website) is licensed under a Creative Commons (CC) license. In short: feel free to reuse the code under MIT; the written/illustrated book content is shared under CC. + ## ♥️ Thanks Thanks for your Star! diff --git a/website/content/ChapterFour/pytool/WordCount.py b/website/content/ChapterFour/pytool/WordCount.py index 3bae213e9..05b3604c5 100644 --- a/website/content/ChapterFour/pytool/WordCount.py +++ b/website/content/ChapterFour/pytool/WordCount.py @@ -1,29 +1,58 @@ -from collections import defaultdict +#!/usr/bin/env python3 +"""Count the total word count of the LeetCode-Go book. + +Metric: Chinese characters (CJK ideographs) + English words, counted over the +authored Markdown only (the book content, the per-problem pages and the +top-level docs). Fenced / inline code blocks are stripped and the third-party +Hugo theme and code templates are excluded, so the number reflects written +prose rather than source code or numeric data. + +The repo root is resolved from this file's location, so it can be run from any +directory: + + python3 website/content/ChapterFour/pytool/WordCount.py +""" import glob import os +import re + +HAN = re.compile(r"[一-鿿]") # CJK unified ideographs (汉字) +EN_WORD = re.compile(r"[A-Za-z]+") # English words +FENCED = re.compile(r"```.*?```", re.S) # ```fenced code blocks``` +INLINE = re.compile(r"`[^`\n]*`") # `inline code` + +# repo root = four levels up from website/content/ChapterFour/pytool/ +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..")) + +# Globs of authored Markdown, relative to the repo root. +GLOBS = [ + "website/content/**/*.md", # the book《LeetCode Cookbook》 + "leetcode/**/*.md", # per-problem pages + "note/*.md", + "*.md", # README and other top-level docs +] + + +def strip_code(text): + """Remove fenced and inline code so only prose is counted.""" + return INLINE.sub(" ", FENCED.sub(" ", text)) + + +def count_words(text): + """汉字 + English words, after stripping code.""" + text = strip_code(text) + return len(HAN.findall(text)) + len(EN_WORD.findall(text)) + + +def main(): + files = sorted({f for g in GLOBS for f in glob.glob(os.path.join(ROOT, g), recursive=True)}) + total = 0 + for file_name in files: + with open(file_name, encoding="utf-8", errors="ignore") as fh: + total += count_words(fh.read()) + print("files counted: {}".format(len(files))) + print("total word count (汉字 + English words, code excluded): {}".format(total)) -def str_count2(str): - count_zh = count_dg = 0 - for s in str: - # 中文字符范围 - if '\u4e00' <= s <= '\u9fff': - count_zh += 1 - if s.isdigit(): - count_dg += 1 - # print(count_zh + count_dg) - return count_zh + count_dg - -current_working_dir = os.getcwd() -# print(f"current_working_dir: {current_working_dir}") - -dir_names = glob.glob("*.md") -dir_names.sort() - -word_count = 0 -for file_name in dir_names: - with open(file_name, "r") as myfile: - codeContent = myfile.read() - print("当前读取文件: {}, 字数统计: {}".format(file_name, str_count2(codeContent))) - word_count += str_count2(codeContent) -print(word_count) \ No newline at end of file +if __name__ == "__main__": + main()