1
+name: Crawl and publish raw data
2
+
3
+on:
4
+ schedule:
5
+ - cron: '0 8 * * 1'
6
+ workflow_dispatch:
7
+
8
+permissions:
9
+ actions: read
10
+ contents: write
11
+
12
+concurrency:
13
+ group: weekly-crawl
14
+ cancel-in-progress: false
15
+
16
+jobs:
17
+ crawl:
18
+ runs-on: ubuntu-latest
19
+
20
+ steps:
21
+ - name: Check out repository
22
+ uses: actions/checkout@v4
23
+ with:
24
+ fetch-depth: 0
25
+ ref: ${{ github.event.repository.default_branch }}
26
+
27
+ - name: Find latest successful crawl cache
28
+ id: previous-cache-run
29
+ uses: actions/github-script@v7
30
+ with:
31
+ script: |
32
+ const workflowId = 'crawl-and-publish.yml';
33
+ const branch = context.payload.repository.default_branch;
34
+ const currentRunId = String(context.runId);
35
+ const perPage = 100;
36
+ let page = 1;
37
+ let previous = null;
38
+
39
+ try {
40
+ while (!previous) {
41
+ const { data } = await github.rest.actions.listWorkflowRuns({
42
+ owner: context.repo.owner,
43
+ repo: context.repo.repo,
44
+ workflow_id: workflowId,
45
+ branch,
46
+ status: 'completed',
47
+ per_page: perPage,
48
+ page,
49
+ });
50
+
51
+ previous = data.workflow_runs.find((run) => run.conclusion === 'success' && String(run.id) !== currentRunId) ?? null;
52
+ if (previous || data.workflow_runs.length < perPage) {
53
+ break;
54
+ }
55
+
56
+ page += 1;
57
+ }
58
+
59
+ core.setOutput('run_id', previous ? String(previous.id) : '');
60
+ core.info(previous ? `Restoring crawl cache from run ${previous.id}.` : 'No previous successful crawl cache found.');
61
+ } catch (error) {
62
+ core.warning(`Skipping cache restore lookup: ${error.message}`);
63
+ core.setOutput('run_id', '');
64
+ }
65
+
66
+ - name: Restore previous crawl cache
67
+ if: steps.previous-cache-run.outputs.run_id != ''
68
+ continue-on-error: true
69
+ uses: actions/download-artifact@v4
70
+ with:
71
+ github-token: ${{ secrets.GITHUB_TOKEN }}
72
+ run-id: ${{ steps.previous-cache-run.outputs.run_id }}
73
+ name: crawl-cache
74
+ path: data/cache/
75
+
76
+ - name: Set up Python
77
+ uses: actions/setup-python@v5
78
+ with:
79
+ python-version: '3.12'
80
+
81
+ - name: Run crawler
82
+ env:
83
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
84
+ run: python scripts/crawl.py
85
+
86
+ - name: Upload raw crawl artifact
87
+ if: always()
88
+ uses: actions/upload-artifact@v4
89
+ with:
90
+ name: crawl-raw
91
+ path: data/raw/
92
+ if-no-files-found: warn
93
+
94
+ - name: Upload snapshot artifact
95
+ if: always()
96
+ uses: actions/upload-artifact@v4
97
+ with:
98
+ name: crawl-snapshots
99
+ path: data/snapshots/
100
+ if-no-files-found: warn
101
+
102
+ - name: Upload cache artifact
103
+ if: always()
104
+ uses: actions/upload-artifact@v4
105
+ with:
106
+ name: crawl-cache
107
+ path: data/cache/
108
+ if-no-files-found: warn
109
+
110
+ - name: Commit crawl data
111
+ env:
112
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
113
+ run: |
114
+ git config user.name "github-actions[bot]"
115
+ git config user.email "github-actions[bot]@users.noreply.github.com"
116
+ if ! git status --short -- data/raw data/snapshots | grep -q .; then
117
+ echo "No crawl data changes to commit."
118
+ exit 0
119
+ fi
120
+ git stash push --include-untracked --message crawl-data -- data/raw data/snapshots
121
+ git fetch origin "$DEFAULT_BRANCH"
122
+ git checkout -B "$DEFAULT_BRANCH" "origin/$DEFAULT_BRANCH"
123
+ git stash pop || {
124
+ echo "Failed to reapply crawl data after syncing $DEFAULT_BRANCH."
125
+ exit 1
126
+ }
127
+ git add data/raw/ data/snapshots/
128
+ git commit -m "data: weekly crawl $(date +%Y-W%V)"
129
+ git push origin "HEAD:$DEFAULT_BRANCH" || {
130
+ echo "Push failed after syncing with $DEFAULT_BRANCH."
131
+ exit 1
132
+ }