|
| 1 | +# 隐私闸 · GitHub 这一侧的第二道(复查,不是拦截) |
| 2 | +# |
| 3 | +# github.com 上没有办法在"收到推送之前"拒绝 —— 那是 GitHub 企业版自建服务器才有的功能。 |
| 4 | +# 这个 Action 在推上来**之后**跑:公开仓库在推上来的那一刻已经能被人看到,它收不回来。 |
| 5 | +# 它的用处是兜住本地那道闸管不到的推送:别的机器推的、--no-verify 绕过的、网页上直接改的。 |
| 6 | +# 失败时 GitHub 会按你的通知设置发邮件。 |
| 7 | +# |
| 8 | +# 用法:把本文件放到仓库的 .github/workflows/privacy-gate.yml,然后 |
| 9 | +# gh secret set PRIVACY_TERMS -R <owner>/<repo> < ~/.compound-memory/privacy-terms.txt |
| 10 | +# gh variable set PRIVACY_MODE -R <owner>/<repo> --body publish # 可选,默认 strict |
| 11 | +# 词表以加密密文存在 GitHub 上,只在这个 Action 运行时可见,不进仓库、不出现在日志里。 |
| 12 | +name: privacy-gate |
| 13 | +on: [push] |
| 14 | +permissions: |
| 15 | + contents: read |
| 16 | +jobs: |
| 17 | + scan: |
| 18 | + runs-on: ubuntu-latest |
| 19 | + steps: |
| 20 | + - uses: actions/checkout@v4 |
| 21 | + with: |
| 22 | + fetch-depth: 0 |
| 23 | + - name: 扫这次推上来的新增内容 |
| 24 | + env: |
| 25 | + TERMS: ${{ secrets.PRIVACY_TERMS }} |
| 26 | + MODE: ${{ vars.PRIVACY_MODE }} |
| 27 | + BEFORE: ${{ github.event.before }} |
| 28 | + AFTER: ${{ github.sha }} |
| 29 | + run: | |
| 30 | + python3 - <<'PY' |
| 31 | + import os, re, subprocess, sys |
| 32 | + terms_txt = os.environ.get('TERMS', '') |
| 33 | + if not terms_txt.strip(): |
| 34 | + print('::error::没有设置 PRIVACY_TERMS 密文,隐私复查没法跑'); sys.exit(1) |
| 35 | + mode = os.environ.get('MODE') or 'strict' |
| 36 | + tier, terms = 'private', [] |
| 37 | + for s in (l.strip() for l in terms_txt.splitlines()): |
| 38 | + if not s or s.startswith('#'): |
| 39 | + continue |
| 40 | + if s in ('[private]', '[brand]'): |
| 41 | + tier = s[1:-1]; continue |
| 42 | + if mode == 'publish' and tier != 'private': |
| 43 | + continue |
| 44 | + try: |
| 45 | + terms.append(re.compile(s)) |
| 46 | + except re.error: |
| 47 | + pass |
| 48 | + before, after = os.environ['BEFORE'], os.environ['AFTER'] |
| 49 | + rng = [after] if set(before) == {'0'} else [f'{before}..{after}'] |
| 50 | + log = subprocess.run(['git', 'log', '-p', '--no-color', '--format=%x00%h %B', *rng], |
| 51 | + capture_output=True, text=True, errors='replace').stdout |
| 52 | + n = 0 |
| 53 | + for line in log.splitlines(): |
| 54 | + if line.startswith(('+++', '---')) or line.startswith('-'): |
| 55 | + continue |
| 56 | + for rx in terms: |
| 57 | + if rx.search(line): |
| 58 | + n += 1 |
| 59 | + # 只报位置和第几条规则,不回显命中的原文 —— Action 日志对有读权限的人可见 |
| 60 | + print(f'::error::第 {n} 处命中(规则 #{terms.index(rx) + 1}),在新增内容里') |
| 61 | + break |
| 62 | + print(f'隐私复查({mode}):词表 {len(terms)} 条,命中 {n} 处') |
| 63 | + sys.exit(1 if n else 0) |
| 64 | + PY |
0 commit comments