Repository navigation
138 lines (132 loc) · 6.17 KB
/
Copy pathpython-ci.yml
File metadata and controls
138 lines (132 loc) · 6.17 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
# This file is part of MediaWiki Code2Code Search
# <https://github.com/ftosoni/mediawiki-code2code-search>.
# Copyright (c) 2026 Francesco Tosoni.
name: Python CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
# translatewiki.org pushes i18n updates daily; queue deploys instead of racing them.
concurrency:
group: toolforge-deploy
cancel-in-progress: false
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Linting dependencies
run: |
python -m pip install --upgrade pip
pip install flake8
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --exclude=tests/example.py
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=tests/example.py
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
# We install dependencies but exclude heavy ones if possible,
# however app.py imports them so we need them for TestClient to work.
pip install -r requirements.txt
pip install pytest pytest-asyncio
- name: Run tests
run: |
# Run structural parser tests for all languages
# These are lightweight and do not require heavy model downloads.
# Also run mocked API tests and connectivity tests.
pytest tests/test_*_parser.py tests/test_api.py tests/test_hf_availability.py
deploy:
name: Deploy to Toolforge
needs: [ lint, test ]
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Rebuild image and restart webservice
uses: appleboy/ssh-action@v1.2.2
with:
host: login.toolforge.org
username: ${{ secrets.TOOLFORGE_USERNAME }}
key: ${{ secrets.TOOLFORGE_SSH_KEY }}
port: 22
# `toolforge build start` streams the build log and only returns once the
# build finishes, so the restart below always picks up the new image.
request_pty: true
command_timeout: 30m
# ssh-action dropped the `script_stop` input in v1.1.0; `set -e` replaces it.
script: |
set -e
T="become code2codesearch toolforge"
# `build clean` wipes ALL images, including the one the running pod uses. That is
# acceptable only because the pod keeps the already-pulled image in memory: as long
# as we never stop/start the webservice without a fresh, successful build, the site
# stays up on the old version. Cleaning every run keeps Harbor usage bounded.
$T build clean -y
# `build start` can exit 0 even when the build fails (e.g. gitlab.wikimedia.org
# returning 500 while fetching a buildpack), so check the status explicitly and
# retry transient failures. If the status line cannot be parsed we fail safe:
# the webservice is left untouched.
ok=0
for try in 1 2 3; do
$T build start https://github.com/ftosoni/mediawiki-code2code-search || true
if $T build show | grep -Eiq '^[[:space:]]*status:[[:space:]]*(ok|succeeded)'; then
ok=1
break
fi
echo "Build failed (attempt ${try}/3), retrying in $((try * 60))s..." >&2
sleep $((try * 60))
done
if [ "$ok" != 1 ]; then
echo "Build failed 3 times: webservice NOT restarted, still serving the previous version." >&2
echo "Do not restart it manually until a build succeeds: its image has been cleaned." >&2
exit 1
fi
$T webservice buildservice stop --mount=all || true
$T webservice buildservice start --mount=all -m 6Gi
# `buildservice start` returns once the pod is scheduled, not once gunicorn has
# loaded the models, which takes minutes. Without this wait the job goes green
# while the site is still serving nothing, or the previous image.
UA="code2codesearch-ci (https://github.com/ftosoni/mediawiki-code2code-search)"
healthy=0
for attempt in $(seq 1 60); do
if curl -fsS -A "$UA" https://code2codesearch.toolforge.org/health | grep -q '"status":"ok"'; then
echo "Webservice healthy after ${attempt} attempt(s)."
healthy=1
break
fi
sleep 10
done
if [ "$healthy" != 1 ]; then
echo "Webservice did not answer /health within 10 minutes." >&2
exit 1
fi
# /health only proves the index is loaded, not that retrieval is sane: in Oct 2026
# unpinned torch/transformers loaded the model in bfloat16 and the gcd query silently
# returned unrelated code. Any index of the MediaWiki ecosystem contains gcd helpers
# (e.g. FormatMetadata::gcd in core), so require at least one in the top 10.
resp=$(curl -fsS --max-time 300 -A "$UA" -H 'Content-Type: application/json' \
-d '{"query":"def gcd(a, b):\n while b:\n a, b = b, a % b\n return a","top_k":10}' \
https://code2codesearch.toolforge.org/search) || resp=""
if printf '%s' "$resp" | grep -qi '"name":"[^"]*gcd'; then
echo "Smoke test passed: the gcd query returns gcd implementations."
else
echo "Smoke test FAILED: no gcd implementation in the top 10 for the gcd query. Top results:" >&2
printf '%s' "$resp" | grep -o '"name":"[^"]*"' | head -3 >&2 || true
exit 1
fi