Repository navigation
同步招聘信息 #358
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: 同步招聘信息 | |
| on: | |
| schedule: | |
| - cron: "17 * * * *" | |
| workflow_dispatch: | |
| inputs: | |
| probe_only: | |
| description: 只验证腾讯文档完整性,不修改内容 | |
| required: false | |
| default: false | |
| type: boolean | |
| accept_source_deletions: | |
| description: 确认将本次缺失记录标记为源表已移除 | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| actions: write | |
| contents: write | |
| issues: write | |
| concurrency: | |
| group: ${{ github.repository }}-qq-jobs-sync | |
| cancel-in-progress: false | |
| env: | |
| DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
| HUGO_VERSION: 0.159.0 | |
| PLAYWRIGHT_VERSION: 1.55.0 | |
| PYTHONDONTWRITEBYTECODE: "1" | |
| jobs: | |
| sync: | |
| name: 校验、同步并发布 | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| steps: | |
| - name: 检出最新默认分支 | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ github.event.repository.default_branch }} | |
| submodules: false | |
| - name: 安装 Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.12" | |
| - name: 还原滚动专用账号会话 | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| QQ_STORAGE_STATE_B64: ${{ secrets.QQ_DOCS_STORAGE_STATE_B64 }} | |
| run: | | |
| if [[ -z "$QQ_STORAGE_STATE_B64" ]]; then | |
| echo "缺少 QQ_DOCS_STORAGE_STATE_B64。" >&2 | |
| exit 1 | |
| fi | |
| STORAGE_STATE="$RUNNER_TEMP/qq-docs-storage-state.json" | |
| SESSION_ARTIFACT_DIR="$RUNNER_TEMP/qq-docs-session-artifacts" | |
| mkdir -p "$SESSION_ARTIFACT_DIR" | |
| session_key="$(printf '%s' "$QQ_STORAGE_STATE_B64" | sha256sum | cut -d ' ' -f 1)" | |
| restored=false | |
| while IFS= read -r run_id; do | |
| candidate_dir="$SESSION_ARTIFACT_DIR/$run_id" | |
| if gh run download "$run_id" \ | |
| --name qq-docs-storage-state \ | |
| --dir "$candidate_dir" >/dev/null 2>&1; then | |
| encrypted_state="$candidate_dir/qq-docs-storage-state.json.enc" | |
| if [[ ! -f "$encrypted_state" ]]; then | |
| continue | |
| fi | |
| if SESSION_KEY="$session_key" openssl enc -d -aes-256-cbc -pbkdf2 \ | |
| -in "$encrypted_state" \ | |
| -out "$STORAGE_STATE" \ | |
| -pass env:SESSION_KEY; then | |
| restored=true | |
| echo "已恢复最近一次成功运行刷新后的专用账号会话。" | |
| break | |
| fi | |
| echo "::warning::滚动会话无法用当前 Secret 解密,继续查找或回退到初始会话。" | |
| fi | |
| done < <( | |
| gh run list \ | |
| --workflow qq-jobs-sync.yml \ | |
| --branch "$DEFAULT_BRANCH" \ | |
| --status success \ | |
| --limit 20 \ | |
| --json databaseId \ | |
| --jq '.[].databaseId' | |
| ) | |
| if [[ "$restored" != "true" ]]; then | |
| printf '%s' "$QQ_STORAGE_STATE_B64" | base64 --decode > "$STORAGE_STATE" | |
| echo "未找到滚动会话,已使用初始专用账号会话。" | |
| fi | |
| chmod 600 "$STORAGE_STATE" | |
| STORAGE_STATE="$STORAGE_STATE" \ | |
| python3 -c 'import json, os; state = json.load(open(os.environ["STORAGE_STATE"], encoding="utf-8")); assert isinstance(state.get("cookies"), list) and isinstance(state.get("origins"), list)' | |
| echo "STORAGE_STATE=$STORAGE_STATE" >> "$GITHUB_ENV" | |
| - name: 安装 Playwright Chromium | |
| run: | | |
| python3 -m pip install --disable-pip-version-check "playwright==$PLAYWRIGHT_VERSION" | |
| python3 -m playwright install --with-deps chromium | |
| - name: 运行同步测试 | |
| run: python3 -m unittest tests.test_sync_qq_jobs | |
| - name: 双遍读取并校验腾讯文档 | |
| shell: bash | |
| run: | | |
| args=( | |
| --storage-state "$STORAGE_STATE" | |
| --timeout-seconds 120 | |
| ) | |
| if [[ "${{ inputs.probe_only || false }}" == "true" ]]; then | |
| args+=(--probe-only) | |
| else | |
| args+=(--check-links) | |
| fi | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.accept_source_deletions || false }}" == "true" ]]; then | |
| args+=(--accept-source-deletions) | |
| fi | |
| python3 scripts/sync_qq_jobs.py "${args[@]}" | |
| - name: 加密刷新后的专用账号会话 | |
| shell: bash | |
| env: | |
| QQ_STORAGE_STATE_B64: ${{ secrets.QQ_DOCS_STORAGE_STATE_B64 }} | |
| run: | | |
| session_key="$(printf '%s' "$QQ_STORAGE_STATE_B64" | sha256sum | cut -d ' ' -f 1)" | |
| SESSION_KEY="$session_key" openssl enc -aes-256-cbc -pbkdf2 -salt \ | |
| -in "$STORAGE_STATE" \ | |
| -out "$RUNNER_TEMP/qq-docs-storage-state.json.enc" \ | |
| -pass env:SESSION_KEY | |
| chmod 600 "$RUNNER_TEMP/qq-docs-storage-state.json.enc" | |
| - name: 保存滚动专用账号会话 | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: qq-docs-storage-state | |
| path: ${{ runner.temp }}/qq-docs-storage-state.json.enc | |
| if-no-files-found: error | |
| retention-days: 30 | |
| - name: 检查生成文件范围 | |
| id: changes | |
| if: ${{ !(github.event_name == 'workflow_dispatch' && inputs.probe_only) }} | |
| shell: bash | |
| run: | | |
| allowed='^(data/jobs/(daily-updates|early-recruitment)\.json|content/docs/jobs/(每日更新|秋招提前批)\.md)$' | |
| changed_files="$(git -c core.quotepath=false diff --name-only)" | |
| unexpected="$(printf '%s\n' "$changed_files" | sed '/^$/d' | grep -Ev "$allowed" || true)" | |
| if [[ -n "$unexpected" ]]; then | |
| echo "同步产生了白名单外改动:" >&2 | |
| echo "$unexpected" >&2 | |
| exit 1 | |
| fi | |
| if [[ -z "$changed_files" ]]; then | |
| echo "updated=false" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "updated=true" >> "$GITHUB_OUTPUT" | |
| git diff --check -- \ | |
| data/jobs/daily-updates.json \ | |
| data/jobs/early-recruitment.json \ | |
| content/docs/jobs/每日更新.md \ | |
| content/docs/jobs/秋招提前批.md | |
| fi | |
| - name: 初始化主题子模块 | |
| if: steps.changes.outputs.updated == 'true' | |
| run: git submodule update --init --recursive | |
| - name: 安装 Hugo | |
| if: steps.changes.outputs.updated == 'true' | |
| uses: peaceiris/actions-hugo@v3 | |
| with: | |
| hugo-version: ${{ env.HUGO_VERSION }} | |
| extended: true | |
| - name: 构建整站 | |
| if: steps.changes.outputs.updated == 'true' | |
| run: hugo --minify | |
| - name: 校验搜索索引收录 | |
| if: steps.changes.outputs.updated == 'true' | |
| run: python3 scripts/check_search_index.py --site public | |
| - name: 提交并推送招聘信息 | |
| if: steps.changes.outputs.updated == 'true' | |
| shell: bash | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add -- \ | |
| data/jobs/daily-updates.json \ | |
| data/jobs/early-recruitment.json \ | |
| content/docs/jobs/每日更新.md \ | |
| content/docs/jobs/秋招提前批.md | |
| git commit -m "更新招聘信息" | |
| git push origin "HEAD:$DEFAULT_BRANCH" | |
| - name: 触发现有 Pages 发布工作流 | |
| if: steps.changes.outputs.updated == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: gh workflow run static.yml --ref "$DEFAULT_BRANCH" | |
| report_failure: | |
| name: 报告同步失败 | |
| needs: sync | |
| if: ${{ always() && needs.sync.result == 'failure' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: 创建或更新故障 Issue | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| GH_REPO: ${{ github.repository }} | |
| RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} | |
| shell: bash | |
| run: | | |
| title='招聘信息同步失败' | |
| printf -v body '招聘信息同步未通过完整性门槛,现有站点内容保持不变。\n\n运行:%s\n触发方式:%s\n提交:%s' \ | |
| "$RUN_URL" "${{ github.event_name }}" "${{ github.sha }}" | |
| issue_number="$(gh issue list --state open --search "$title in:title" --json number,title --jq '.[] | select(.title == "招聘信息同步失败") | .number' | head -n 1)" | |
| if [[ -n "$issue_number" ]]; then | |
| gh issue edit "$issue_number" --body "$body" | |
| else | |
| gh issue create --title "$title" --body "$body" | |
| fi |