-
Notifications
You must be signed in to change notification settings - Fork 808
193 lines (171 loc) · 8.08 KB
/
Copy pathon_schedule_tests.yaml
File metadata and controls
193 lines (171 loc) · 8.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
name: Scheduled tests
on:
# Runs when manually triggered from the GitHub UI.
workflow_dispatch:
inputs:
slack_test:
description: 'Skip the tests and only send a Slack notification, to check the alerting path.'
type: boolean
default: false
# Runs on weekdays at 01:00 UTC.
schedule:
- cron: '0 1 * * 1-5'
# Runs on pull requests that touch the template files, the template e2e tests,
# or this workflow itself, so that changes affecting the scheduled tests are
# validated before they are merged.
pull_request:
paths:
- 'src/crawlee/project_template/**'
- 'tests/e2e/**'
- '.github/workflows/on_schedule_tests.yaml'
concurrency:
group: scheduled-tests
cancel-in-progress: false
permissions:
contents: read
env:
NODE_VERSION: 24
PYTHON_VERSION: 3.14
TESTS_CONCURRENCY: 1
jobs:
end_to_end_tests:
name: End-to-end tests
if: github.event_name != 'workflow_dispatch' || !inputs.slack_test
strategy:
fail-fast: false
max-parallel: 12
matrix:
crawler-type: ["playwright_camoufox", "playwright_chrome", "playwright_firefox", "playwright_webkit", "playwright", "parsel", "beautifulsoup", "adaptive_beautifulsoup", "adaptive_parsel", "stagehand"]
http-client: ["httpx", "curl_impersonate", "impit"]
package-manager: ["pip", "uv", "poetry"]
runs-on: "ubuntu-latest"
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Apify CLI
uses: apify/setup-apify-cli-action@v1.1.0
with:
token: ${{ secrets.APIFY_TEST_USER_API_TOKEN }}
- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: ${{ env.PYTHON_VERSION }}
# installed to be able to patch crawlee in the poetry.lock with custom wheel file for poetry based templates
- name: Install poetry
run: pipx install poetry
- name: Set up uv package manager
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
python-version: ${{ env.PYTHON_VERSION }}
# Sync the project, but no need to install the browsers into the test runner environment.
- name: Install Python dependencies
run: uv run poe install-sync
- name: Run templates end-to-end tests
run: uv run poe e2e-templates-tests -m "${{ matrix.http-client }} and ${{ matrix.crawler-type }} and ${{ matrix.package-manager }}"
env:
APIFY_TEST_USER_API_TOKEN: ${{ secrets.APIFY_TEST_USER_API_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
# Send a Slack notification to the team alerting channel when scheduled e2e tests fail.
# Only scheduled runs notify, so that pull requests and ad-hoc triggers don't spam the channel.
# The `slack_test` dispatch input skips the tests and notifies anyway, which is the only way to
# exercise this path without waiting for a real failure.
notify_on_failure:
name: Notify Slack on failure
needs: end_to_end_tests
if: always() && ((github.event_name == 'schedule' && needs.end_to_end_tests.result == 'failure') || (github.event_name == 'workflow_dispatch' && inputs.slack_test))
runs-on: ubuntu-latest
permissions:
contents: read
actions: read
steps:
- name: Build Slack payload
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
HEADING: "${{ inputs.slack_test && ':white_circle: Slack alerting check, no tests were run' || ':red_circle: Scheduled e2e tests failed' }}"
run: |
# Retry the API call to tolerate transient 5xx from GitHub. The matrix nearly fills a
# single 100-item page, so paginate rather than silently count only the first one, and
# flatten the pages into the same shape a single page has. A response that is empty or
# not shaped as expected counts as a failed attempt as well, so that it falls back
# instead of aborting the step.
max_attempts=5
fetched=0
for attempt in $(seq 1 "${max_attempts}"); do
if gh api --paginate --slurp \
"repos/${REPO}/actions/runs/${RUN_ID}/attempts/${RUN_ATTEMPT}/jobs?per_page=100" > pages.json \
&& jq '{jobs: [.[].jobs[]]}' pages.json > jobs.json \
&& jq -e '.jobs | length > 0 and all(.[]; type == "object")' jobs.json > /dev/null; then
fetched=1
break
fi
if [[ "${attempt}" -lt "${max_attempts}" ]]; then
sleep "$((attempt * 5))"
fi
done
if [[ "${fetched}" -eq 0 ]]; then
echo "Failed to fetch job list after ${max_attempts} attempts; sending notification without it." >&2
failed_jobs="(unable to fetch job list - see workflow run)"
else
# This job is still running and so carries no conclusion, which keeps it out of both
# counts without having to match on its own name.
succeeded=$(jq '[.jobs[] | select(.conclusion == "success")] | length' jobs.json)
failed=$(jq '[.jobs[] | select(.conclusion == "failure")] | length' jobs.json)
bullets=$(jq -r '[.jobs[] | select(.conclusion == "failure") | "- \(.name)"] | join("\n")' jobs.json)
# Slack rejects the whole message with `invalid_blocks` once a section's text passes
# 3000 characters, which a full matrix failure comfortably does. Cut the list to fit,
# drop the partial line the cut leaves behind, and count off the remainder instead,
# leaving room for the count line and the trailing summary.
max_list_chars=2500
if [[ "${#bullets}" -gt "${max_list_chars}" ]]; then
bullets="${bullets:0:${max_list_chars}}"
bullets="${bullets%$'\n'*}"
# The list carries one newline fewer than it has lines, so stripping everything but
# the newlines counts what is left.
newlines="${bullets//[!$'\n']/}"
shown=$((${#newlines} + 1))
bullets="${bullets}"$'\n'"... and $((failed - shown)) more"
fi
failed_jobs="${failed} failed, ${succeeded} succeeded${bullets:+$'\n'}${bullets}"
fi
jq -n \
--arg repo "${REPO}" \
--arg url "${WORKFLOW_URL}" \
--arg heading "${HEADING}" \
--arg failed "${failed_jobs}" \
'{
text: "\($heading) in \($repo)",
blocks: [
{
type: "header",
text: { type: "plain_text", text: $heading, emoji: true }
},
{
type: "section",
fields: [
{ type: "mrkdwn", text: "*Repository:*\n\($repo)" },
{ type: "mrkdwn", text: "*Workflow run:*\n<\($url)|View on GitHub>" }
]
},
{
type: "section",
text: { type: "mrkdwn", text: "*Failed jobs:*\n\($failed)" }
}
]
}' > slack-payload.json
- name: Send Slack notification
uses: slackapi/slack-github-action@dcb1066f776dd043e64d0e8ba94ca15cc7e1875d # v4.0.0
with:
# Without this the action swallows every delivery error into a hidden debug log line and
# reports success, so a rejected or undeliverable alert looks exactly like a sent one.
errors: true
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload-file-path: slack-payload.json