开发技巧

用 GitHub Pages 搭一个小组周报网站

用 GitHub Pages 搭一个小组周报网站:周报用 Markdown 写在仓库里,推送到 main 后 GitHub Actions 自动构建成 HTML、检查站内断链并发布。

BINBHO · 图文教程

用 GitHub Pages 搭一个小组周报网站:周报用 Markdown 写在仓库里,推送到 main 后 GitHub Actions 自动构建成 HTML、检查站内断链并发布。

先看清成品

build.py 已在本机运行:第一次构建发现 1 个断链并以退出码 1 失败(见调试示例),修复后构建 2 页,并用本地 HTTP 服务器确认页面标题、导航和表格正确。pages.yml 已通过 actionlint 和 GitHub workflow schema 校验。没有推送到 GitHub 实际部署 Pages。

注意:遵守 GitHub 服务条款。不要在仓库、日志或 issue 里提交密钥和个人数据;不要用 Actions 挖矿或做与仓库无关的大量计算;不要共享账号。

Pages 内容规范:GitHub Pages 不能用于商业交易站点或违法内容,具体以 GitHub Pages 使用限制为准。

做完之后你会得到什么?

成果 说明
周报站 https://<用户名>.github.io/<仓库名>/
构建脚本 Markdown → HTML,自动生成导航
断链检查 有断链就不发布
部署 workflow 构建与部署分两个任务

需要准备哪些环境?

项目 要求 说明
GitHub 仓库 公开仓库可免费用 Pages 私有仓库用 Pages 需要付费计划,以 GitHub 文档为准
Python 3.11 以上 本地预览
Python-Markdown 3.11.2 写在 requirements.txt

文件结构

19-github-B-src/
├── build.py
├── requirements.txt
├── content/
│   ├── index.md
│   └── 2026-w42.md
└── .github/workflows/pages.yml

第 1 步:写内容

content/index.md:

# 首页

本站每周发布一次小组周报。最新一期见 [第 42 周](2026-w42.md)。

| 周次 | 主题 |
| :--- | :--- |
| 42 | 发布流程迁移到 GitHub Actions |

content/2026-w42.md:

# 2026 年第 42 周

- 文档站改为 Actions 部署
- 下周计划:整理部署文档

[返回首页](index.md)

写作时站内链接写 xxx.md,在 GitHub 上直接浏览也能点;构建时自动改成 .html。

第 2 步:构建脚本

build.py:

"""把 content/*.md 构建成 _site/*.html,并检查站内链接。"""
import html, re, sys
from pathlib import Path
import markdown

SRC, OUT = Path("content"), Path("_site")
TPL = """<!doctype html>
<html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>{title} · 小组周报</title>
<style>body{{max-width:46rem;margin:2rem auto;padding:0 1rem;font:16px/1.7 system-ui,sans-serif}}
nav a{{margin-right:1rem}}</style></head>
<body><nav>{nav}</nav><main>{body}</main></body></html>
"""

def title_of(text, fallback):
    m = re.search(r"^# (.+)$", text, re.M)
    return m.group(1).strip() if m else fallback

def build():
    OUT.mkdir(exist_ok=True)
    pages = sorted(SRC.glob("*.md"))
    nav = " ".join(f'<a href="{p.stem}.html">{html.escape(title_of(p.read_text(encoding="utf-8"), p.stem))}</a>' for p in pages)
    for p in pages:
        text = p.read_text(encoding="utf-8")
        # 写作时链接写 xxx.md,构建时改成 xxx.html
        body = markdown.markdown(re.sub(r"\]\(([\w-]+)\.md\)", r"](\1.html)", text), extensions=["tables"])
        (OUT / f"{p.stem}.html").write_text(TPL.format(title=html.escape(title_of(text, p.stem)), nav=nav, body=body), encoding="utf-8")
    (OUT / ".nojekyll").write_text("")
    return pages

def check_links():
    bad = []
    for f in OUT.glob("*.html"):
        for href in re.findall(r'href="([^"#:]+)"', f.read_text(encoding="utf-8")):
            if not (OUT / href).exists():
                bad.append(f"{f.name} -> {href}")
    return bad

if __name__ == "__main__":
    pages = build()
    bad = check_links()
    print(f"构建 {len(pages)} 页到 {OUT}/")
    for b in bad: print("✗ 断链:", b)
    sys.exit(1 if bad else 0)

.nojekyll 告诉 Pages 不要再用 Jekyll 处理;所有链接都是相对路径,所以放在 /仓库名/ 子路径下也能用。

第 3 步:本地构建预览

pip install -r requirements.txt
python build.py
cd _site && python -m http.server 8000

检查结果:终端输出 构建 2 页到 _site/;浏览器打开 http://localhost:8000/index.html,能看到导航和表格。

第 4 步:部署 workflow

.github/workflows/pages.yml:

name: Deploy Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: "3.13"
          cache: pip
      - run: pip install -r requirements.txt
      - name: 构建并检查链接
        run: python build.py
      - uses: actions/configure-pages@v6
      - uses: actions/upload-pages-artifact@v5
        with:
          path: _site

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v5

deploy-pages 所在任务必须有 pages: write 和 id-token: write,并部署到 github-pages 环境。

第 5 步:打开 Pages 并发布

  1. 仓库 Settings → Pages,Source 选 GitHub Actions。
  2. 推送到 main。
  3. 在 Actions 里看到 build、deploy 两个任务都成功后,deploy 任务上会显示站点地址。

调试示例

最初 2026-w42.md 里写了指向尚不存在的第 43 周的链接,本机实际输出:

构建 2 页到 _site/
✗ 断链: 2026-w42.html -> 2026-w43.html

退出码为 1,在 CI 里 build 任务会失败,deploy 不会执行,线上站点保持旧版本。删掉链接(或先写好第 43 周)后通过。

出错了怎么办?

现象 原因 解决
deploy 报权限错误 缺 pages: write / id-token: write 按本文 permissions 写
站点 404 Settings → Pages 的 Source 没选 GitHub Actions 改成 GitHub Actions
样式或链接丢失 用了 /xxx.html 绝对路径 改成相对路径
部署被环境规则拒绝 github-pages 环境只允许特定分支 在 Settings → Environments 检查分支规则
中文乱码 文件不是 UTF-8 统一 UTF-8 保存

模板规则

  • 一期周报一个 Markdown 文件,文件名 YYYY-wNN.md。
  • 断链检查不通过不发布。
  • 依赖版本固定在 requirements.txt。

项目资源

用途 地址
用自定义 workflow 发布 Pages https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages
actions/deploy-pages https://github.com/actions/deploy-pages
Python-Markdown https://python-markdown.github.io/
actionlint https://github.com/rhysd/actionlint

这个站点只有一个构建脚本和一个 workflow,没有引入框架。周报写完推送就上线,构建出错会挡住发布,线上版本始终是能用的。