Skip to content

Commit 1f2d711

Browse files
splchclaude
andcommitted
Improve CI/CD logging and add missing dependencies
- Add detailed logging to GitHub Actions workflow with step-by-step output - Add environment verification step to show Python version and packages - Add test summary step that runs even if tests fail - Enhance test_notebooks.py with detailed execution logging: - Show timestamp and duration for each notebook - Display progress with visual separators - Provide summary with pass/fail counts - Add better error reporting - Add pytest and nbconvert to environment.yml (required for tests) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 8900435 commit 1f2d711

File tree

3 files changed

+111
-7
lines changed

3 files changed

+111
-7
lines changed

.github/workflows/test_notebooks.yml

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,61 @@ jobs:
2626
- name: Prepare Conda environment
2727
shell: bash -l {0}
2828
run: |
29+
echo "=================================================="
30+
echo "Preparing Conda environment"
31+
echo "=================================================="
2932
if conda env list | grep -q 'myenv'; then
30-
echo "Environment 'myenv' already exists, updating environment"
33+
echo "Environment 'myenv' already exists, updating environment"
3134
conda env update --name myenv --file environment.yml --prune
3235
else
33-
echo "Creating new environment 'myenv'"
36+
echo "Creating new environment 'myenv'"
3437
conda env create -f environment.yml
3538
fi
39+
echo "✓ Conda environment ready"
40+
41+
- name: Verify environment
42+
shell: bash -l {0}
43+
run: |
44+
echo "=================================================="
45+
echo "Verifying environment setup"
46+
echo "=================================================="
47+
conda activate myenv
48+
echo "✓ Python version:"
49+
python --version
50+
echo ""
51+
echo "✓ Installed packages:"
52+
conda list | grep -E "(jupyter|qiskit|cirq|pennylane|qbraid|requests)"
53+
echo ""
54+
echo "✓ IONQ_API_KEY status:"
55+
if [ -n "$IONQ_API_KEY" ]; then
56+
echo " API key is set (length: ${#IONQ_API_KEY} characters)"
57+
else
58+
echo " WARNING: API key is not set!"
59+
fi
60+
env:
61+
IONQ_API_KEY: ${{ secrets.IONQ_API_KEY }}
3662

3763
- name: Run notebook tests
3864
shell: bash -l {0}
3965
run: |
66+
echo "=================================================="
67+
echo "Running notebook tests"
68+
echo "=================================================="
4069
conda activate myenv
4170
python tests/test_notebooks.py
4271
env:
4372
IONQ_API_KEY: ${{ secrets.IONQ_API_KEY }}
73+
NBEXEC_TIMEOUT_SECONDS: 600
74+
75+
- name: Test summary
76+
if: always()
77+
shell: bash -l {0}
78+
run: |
79+
echo "=================================================="
80+
echo "Test Execution Summary"
81+
echo "=================================================="
82+
if [ ${{ job.status }} == 'success' ]; then
83+
echo "✓ All notebook tests passed successfully!"
84+
else
85+
echo "✗ Some tests failed. Check logs above for details."
86+
fi

environment.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ dependencies:
66
- python=3.11
77
- pip
88
- jupyter
9+
- pytest
10+
- nbconvert
911
# Add other dependencies here, for Conda packages.
1012
- pip:
1113
# Pip-only packages here, if any.

tests/test_notebooks.py

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010
import subprocess
1111
import sys
12+
import time
1213

1314
import pytest
1415

@@ -25,6 +26,13 @@ def _run_notebook(path: str) -> None:
2526
Raises:
2627
AssertionError: If the notebook execution fails.
2728
"""
29+
print(f"\n{'='*60}")
30+
print(f"Executing: {path}")
31+
print(f"Timeout: {NOTEBOOK_TIMEOUT_SECONDS}s")
32+
print(f"{'='*60}")
33+
34+
start_time = time.time()
35+
2836
cmd = [
2937
sys.executable,
3038
"-m",
@@ -36,19 +44,31 @@ def _run_notebook(path: str) -> None:
3644
f"--ExecutePreprocessor.timeout={NOTEBOOK_TIMEOUT_SECONDS}",
3745
path,
3846
]
47+
48+
print(f"Running command: {' '.join(cmd)}")
49+
print(f"Starting execution at {time.strftime('%Y-%m-%d %H:%M:%S')}")
50+
3951
result = subprocess.run(
4052
cmd,
4153
stdout=subprocess.PIPE,
4254
stderr=subprocess.PIPE,
4355
text=True,
4456
)
4557

58+
elapsed_time = time.time() - start_time
59+
4660
if result.returncode != 0:
61+
print(f"✗ FAILED after {elapsed_time:.2f}s")
62+
print(f"\nSTDOUT:\n{result.stdout}")
63+
print(f"\nSTDERR:\n{result.stderr}")
4764
raise AssertionError(
48-
f"Error executing notebook {path}.\n\n"
65+
f"Error executing notebook {path} after {elapsed_time:.2f}s.\n\n"
4966
f"STDOUT:\n{result.stdout}\n\nSTDERR:\n{result.stderr}"
5067
)
5168

69+
print(f"✓ PASSED in {elapsed_time:.2f}s")
70+
print(f"Completed at {time.strftime('%Y-%m-%d %H:%M:%S')}")
71+
5272

5373
@pytest.mark.skipif(
5474
not IONQ_API_KEY,
@@ -66,17 +86,56 @@ def test_notebooks() -> None:
6686
Raises:
6787
AssertionError: If no notebooks are found or if any notebook fails.
6888
"""
69-
notebooks = glob.glob("**/*.ipynb", recursive=True)
89+
notebooks = sorted(glob.glob("**/*.ipynb", recursive=True))
90+
91+
print(f"\n{'='*60}")
92+
print(f"Found {len(notebooks)} notebook(s) to test")
93+
print(f"{'='*60}")
94+
for i, nb in enumerate(notebooks, 1):
95+
print(f"{i}. {nb}")
96+
7097
assert notebooks, "No notebooks found to test."
7198

99+
total_start = time.time()
100+
passed = 0
101+
failed = 0
102+
72103
for notebook in notebooks:
73-
print(f"Testing {notebook}")
74-
_run_notebook(notebook)
104+
try:
105+
_run_notebook(notebook)
106+
passed += 1
107+
except AssertionError as e:
108+
failed += 1
109+
if not os.getenv("PYTEST_CURRENT_TEST"):
110+
# Only re-raise if not running under pytest
111+
raise
112+
113+
total_elapsed = time.time() - total_start
114+
115+
print(f"\n{'='*60}")
116+
print(f"Test Summary")
117+
print(f"{'='*60}")
118+
print(f"Total notebooks: {len(notebooks)}")
119+
print(f"✓ Passed: {passed}")
120+
print(f"✗ Failed: {failed}")
121+
print(f"Total time: {total_elapsed:.2f}s")
122+
print(f"{'='*60}\n")
123+
124+
if failed > 0:
125+
raise AssertionError(f"{failed} notebook(s) failed to execute")
75126

76127

77128
if __name__ == "__main__":
129+
print(f"\n{'='*60}")
130+
print(f"Notebook Integration Test Runner")
131+
print(f"{'='*60}")
132+
print(f"Python version: {sys.version}")
133+
print(f"Timeout per notebook: {NOTEBOOK_TIMEOUT_SECONDS}s")
134+
print(f"API Key configured: {'Yes' if IONQ_API_KEY else 'No'}")
135+
print(f"{'='*60}\n")
136+
78137
if not IONQ_API_KEY:
79-
print("IONQ_API_KEY not set; skipping notebook tests.")
138+
print("IONQ_API_KEY not set; skipping notebook tests.")
80139
raise SystemExit(0)
81140

82141
test_notebooks()

0 commit comments

Comments
 (0)