Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4e76dadd98 | |||
| 56ea9ff7b8 |
+72
-57
@@ -158,57 +158,72 @@ jobs:
|
|||||||
python3 scripts/check_migration_safety.py --allow-medium-risk
|
python3 scripts/check_migration_safety.py --allow-medium-risk
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Debug coverage paths
|
unit-tests:
|
||||||
shell: sh
|
name: Unit Tests
|
||||||
run: |
|
runs-on: host
|
||||||
set +e
|
timeout-minutes: 8
|
||||||
echo "=== PWD ==="
|
|
||||||
pwd
|
|
||||||
echo "=== check source dirs ==="
|
|
||||||
ls -d apps/api/app packages
|
|
||||||
echo "=== python import check ==="
|
|
||||||
python3 - <<'PY'
|
|
||||||
import sys, os
|
|
||||||
os.environ["PYTHONPATH"] = f"{os.getcwd()}/apps/api:{os.getcwd()}"
|
|
||||||
sys.path.insert(0, f"{os.getcwd()}/apps/api")
|
|
||||||
sys.path.insert(0, os.getcwd())
|
|
||||||
print(f"cwd: {os.getcwd()}")
|
|
||||||
print(f"sys.path[:5]: {sys.path[:5]}")
|
|
||||||
try:
|
|
||||||
import app
|
|
||||||
print(f"app.__file__: {app.__file__}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"import app failed: {e}")
|
|
||||||
try:
|
|
||||||
import packages
|
|
||||||
print(f"packages.__file__: {packages.__file__}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"import packages failed: {e}")
|
|
||||||
PY
|
|
||||||
echo "=== coverage debug ==="
|
|
||||||
python3 - <<'PY'
|
|
||||||
import os, sys
|
|
||||||
sys.path.insert(0, f"{os.getcwd()}/apps/api")
|
|
||||||
sys.path.insert(0, os.getcwd())
|
|
||||||
import coverage
|
|
||||||
cov = coverage.Coverage(source=["apps/api/app", "packages"])
|
|
||||||
print(f"source: {cov.config.source}")
|
|
||||||
for src in cov.config.source or []:
|
|
||||||
abspath = os.path.abspath(src)
|
|
||||||
print(f" {src} -> {abspath} exists={os.path.exists(src)}")
|
|
||||||
if os.path.isdir(src):
|
|
||||||
pyfiles = []
|
|
||||||
for root, dirs, files in os.walk(src):
|
|
||||||
for f in files:
|
|
||||||
if f.endswith('.py'):
|
|
||||||
pyfiles.append(os.path.join(root, f))
|
|
||||||
print(f" .py files: {len(pyfiles)}")
|
|
||||||
PY
|
|
||||||
|
|
||||||
- name: Run unit tests
|
env:
|
||||||
|
USE_IN_MEMORY_DB: "true"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
shell: sh
|
shell: sh
|
||||||
env:
|
env:
|
||||||
USE_IN_MEMORY_DB: "true"
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
python3 - <<'PY'
|
||||||
|
import io, os, tarfile, time, urllib.request, urllib.error
|
||||||
|
url = f"{os.environ['GITHUB_API_URL']}/repos/{os.environ['GITHUB_REPOSITORY']}/archive/{os.environ['GITHUB_SHA']}.tar.gz"
|
||||||
|
request = urllib.request.Request(url, headers={"Authorization": f"token {os.environ['GITHUB_TOKEN']}"})
|
||||||
|
last_err = None
|
||||||
|
for attempt in range(5):
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(request, timeout=120) as response:
|
||||||
|
archive = response.read()
|
||||||
|
break
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
last_err = e
|
||||||
|
if e.code >= 500 and attempt < 4:
|
||||||
|
wait = 2 ** attempt
|
||||||
|
print(f"Checkout HTTP {e.code}, retrying in {wait}s (attempt {attempt+1}/5)...")
|
||||||
|
time.sleep(wait)
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
last_err = e
|
||||||
|
if attempt < 4:
|
||||||
|
wait = 2 ** attempt
|
||||||
|
print(f"Checkout error: {e}, retrying in {wait}s (attempt {attempt+1}/5)...")
|
||||||
|
time.sleep(wait)
|
||||||
|
continue
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
raise last_err
|
||||||
|
with tarfile.open(fileobj=io.BytesIO(archive), mode='r:gz') as tar:
|
||||||
|
root_prefix = tar.getmembers()[0].name.split('/', 1)[0] + '/'
|
||||||
|
for member in tar.getmembers():
|
||||||
|
name = member.name
|
||||||
|
if name == root_prefix[:-1]:
|
||||||
|
continue
|
||||||
|
if name.startswith(root_prefix):
|
||||||
|
member.name = name[len(root_prefix):]
|
||||||
|
if member.name:
|
||||||
|
tar.extract(member, '.')
|
||||||
|
PY
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: sh
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
python3 -m pip install -q -r requirements-base.txt
|
||||||
|
python3 -m pip install -q -r requirements.txt
|
||||||
|
python3 -m pip install -q -r requirements-dev.txt
|
||||||
|
pytest --version
|
||||||
|
|
||||||
|
- name: Run unit tests with coverage
|
||||||
|
shell: sh
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set -eu
|
||||||
PYTHONPATH="$PWD/apps/api:$PWD" python3 -m coverage run \
|
PYTHONPATH="$PWD/apps/api:$PWD" python3 -m coverage run \
|
||||||
@@ -220,16 +235,16 @@ jobs:
|
|||||||
python3 -m coverage xml -o coverage.xml
|
python3 -m coverage xml -o coverage.xml
|
||||||
python3 -m coverage report --fail-under=60 > /dev/null
|
python3 -m coverage report --fail-under=60 > /dev/null
|
||||||
|
|
||||||
- name: Build summary
|
- name: CI failure notification
|
||||||
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
|
if: failure()
|
||||||
shell: sh
|
shell: sh
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
CI_WEBHOOK_URL: ${{ secrets.CI_WEBHOOK_URL }}
|
||||||
run: |
|
run: |
|
||||||
set -eu
|
set +e
|
||||||
echo "Build completed successfully!"
|
FAILED_JOB="Unit Tests" python3 scripts/ci_notify_failure.py
|
||||||
echo "Branch: ${GITHUB_REF_NAME}"
|
|
||||||
echo "Commit: ${GITHUB_SHA}"
|
|
||||||
# 输出最终覆盖率
|
|
||||||
python3 scripts/ci_coverage_summary.py
|
|
||||||
|
|
||||||
integration-tests:
|
integration-tests:
|
||||||
name: Integration Tests
|
name: Integration Tests
|
||||||
@@ -578,7 +593,7 @@ jobs:
|
|||||||
timeout-minutes: 30
|
timeout-minutes: 30
|
||||||
needs: [validate, frontend-lint]
|
needs: [validate, frontend-lint]
|
||||||
|
|
||||||
if: github.ref_name == 'main' || github.ref_name == 'develop' || startsWith(github.ref_name, 'feature/')
|
if: github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'develop')
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ class TestProbeVideoInfoTimeout:
|
|||||||
"""正常情况应解析 ffprobe JSON 输出。"""
|
"""正常情况应解析 ffprobe JSON 输出。"""
|
||||||
fake_output = """
|
fake_output = """
|
||||||
{
|
{
|
||||||
"streams": [{"width": 1920, "height": 1080, "r_frame_rate": "30/1", "duration": "10.5"}],
|
"streams": [{"width": 1920, "height": 1080, "codec_type": "video", "r_frame_rate": "30/1", "duration": "10.5"}],
|
||||||
"format": {"duration": "10.5"}
|
"format": {"duration": "10.5"}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user