From 9a8113a48c99ddeb44908ae46357487d703178cd Mon Sep 17 00:00:00 2001 From: CyC2018 Date: Sat, 31 Oct 2026 14:31:33 +0800 Subject: [PATCH] Update build.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 构建网站时一起生成 /learn/ 学习站,首页和侧边栏加入口。 --- website/builder/build.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/website/builder/build.py b/website/builder/build.py index 40c3d41e..7d2268f6 100644 --- a/website/builder/build.py +++ b/website/builder/build.py @@ -6,9 +6,12 @@ import argparse import hashlib import html import json +import os import posixpath import re import shutil +import subprocess +import sys import tempfile import unicodedata import urllib.parse @@ -28,6 +31,9 @@ SOURCE_COMMIT = json.loads(VERSION_FILE.read_text())['commit'] if VERSION_FILE.i IMAGE_BY_NAME = {} ANCHORS = {} SOURCE_IMAGE_HOST = 'cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com' +# 学习站与笔记网站一起构建,放在 /learn/ 下;题目链接指向同一域名下的笔记 +LEARN_BUILD = BASE.parent / 'learn' / 'build.py' +SITE_URL = 'https://cyc2018.xyz/' IMAGE = re.compile(r'(]*?\bsrc=["\'])([^"\']+)(["\'])', re.I) MD_IMAGE = re.compile(r'(!\[[^\]]*\]\()((?:[^()\s]|\([^()\n]*\))*)(\))') MD_LINK = re.compile(r'(?' homepage = re.sub(r'^\| (?:学习路线|算法)[^\n]*\n\|[^\n]*\n\|[^\n]*', cards, homepage, count=1, flags=re.M) homepage = homepage.replace('width="200px"', 'class="site-logo" width="200px"', 1) + if LEARN_BUILD.is_file(): + banner = ('按计划学习' + '先用面试真题自评定级,跳过已经掌握的内容,按天学、按间隔复习,记到面试那天' + '开始') + homepage = homepage.replace(cards, banner + cards, 1) (dist / 'README.md').write_text('# CS-Notes\n\n' + homepage) note_paths = ['/' + str(p.relative_to(ROOT).with_suffix('')) for p in sorted((ROOT / 'notes').glob('*.md'))] - sidebar = ['- [首页](/)', ''] + sidebar = ['- [首页](/)', *(["- [学习站:定级与每日计划](/learn/ ':ignore')"] if LEARN_BUILD.is_file() else []), ''] for line in homepage.splitlines(): if line.startswith('## '): label = re.sub(r':[a-z0-9_]+:', '', line[3:]).strip() @@ -220,6 +231,20 @@ def build(optimize=True): (dist / '_404.md').write_text('# 页面暂未找到\n\n请从目录或搜索查找笔记。\n\n[返回首页](/)\n') (dist / '.nojekyll').touch() shutil.copyfile(ROOT / 'docs/_media/LogoMakr_1J56bI.png', dist / 'favicon.png') + learn_report = None + if LEARN_BUILD.is_file(): + # 学习站的问题(链接到的小节改名、题目和要点对不上)只给警告,不挡住笔记的发布;警告写进构建报告 + result = subprocess.run([sys.executable, str(LEARN_BUILD), '--source', str(ROOT), '--output', str(dist / 'learn'), + '--notes-base', '/#/notes/', '--site-url', SITE_URL + 'learn/', '--lenient'], + check=True, capture_output=True, text=True) + learn_report = json.loads(result.stdout.strip().splitlines()[-1]) + # 在 GitHub Actions 中输出为警告注释,显示在运行结果的摘要里 + prefix = '::warning::' if os.environ.get('GITHUB_ACTIONS') else '' + for warning in learn_report['warnings']: + print(prefix + '学习站:' + warning, file=sys.stderr) + if learn_report['pending']: + print(prefix + '学习站:内容与 learn/content.lock.json 不一致,请在本机运行 python3 learn/maintain.py 查看并写入:' + + json.dumps(learn_report['pending'], ensure_ascii=False), file=sys.stderr) copy_referenced_assets(dist, ROOT) image_report = optimize_images(dist, CACHE) if optimize else {'converted': 0} index_name, search_report = write_search_index(dist, sorted((dist / 'notes').glob('*.md'))) @@ -231,9 +256,10 @@ def build(optimize=True): 'window.CS_NOTES_SEARCH_INDEX = "/' + index_name + '";\n' 'window.CS_NOTES_ANIMATIONS = ' + json.dumps(animations, ensure_ascii=False, separators=(',', ':')) + ';\n') generator = hashlib.sha256() - for resource in sorted([*Path(__file__).parent.glob('*.py'), *SITE.rglob('*')]): + learn = [*LEARN_BUILD.parent.glob('*.py'), *(LEARN_BUILD.parent / 'src').rglob('*')] if LEARN_BUILD.is_file() else [] + for resource in sorted([*Path(__file__).parent.glob('*.py'), *SITE.rglob('*'), *learn]): if resource.is_file(): - generator.update(resource.relative_to(BASE).as_posix().encode()) + generator.update(resource.relative_to(BASE.parent).as_posix().encode()) generator.update(resource.read_bytes()) build_info = {'sourceCommit': SOURCE_COMMIT, 'generatorHash': generator.hexdigest(), 'notes': len(note_paths), 'searchIndex': index_name, 'legacyRoutes': len(legacy)} @@ -247,7 +273,7 @@ def build(optimize=True): files = [p for p in dist.rglob('*') if p.is_file()] report = {'notes': len(note_paths), 'files': len(files), 'bytes': sum(p.stat().st_size for p in files), 'oversized': [str(p.relative_to(dist)) for p in files if p.stat().st_size > 25*1024*1024], - 'repositoryImagesUsedForOldUrls': len(reused), 'externalImages': len(unchanged_external), 'search': search_report, 'images': image_report, 'legacyRoutes': len(legacy)} + 'learn': learn_report, 'repositoryImagesUsedForOldUrls': len(reused), 'externalImages': len(unchanged_external), 'search': search_report, 'images': image_report, 'legacyRoutes': len(legacy)} if report['oversized'] or len(files) > 20000: raise ValueError('Static hosting quota exceeded') validation = check(ROOT, dist)