11name : CI
22
3- # Integration Test Strategy:
4- # - Fork PRs: Cannot access secrets, so integration tests are skipped with informative feedback
5- # - Same-repo PRs: Have access to secrets, integration tests run normally
6- # - Push to main/develop: Integration tests always run to catch any issues after merge
7- # - Manual trigger: Allows maintainers to run integration tests on demand
8- #
9- # This ensures security while still validating integration tests before release
10-
113on :
124 push :
135 branches : [ main, develop ]
146 pull_request :
15- branches : [ main, develop ]
16- # Run integration tests after PR is merged
17- workflow_dispatch : # Allow manual trigger for integration tests
7+ branches : [ main ]
188
199jobs :
20- test :
10+ lint-and-type-check :
2111 runs-on : ubuntu-latest
22- strategy :
23- matrix :
24- python-version : ['3.10', '3.11', '3.12']
2512
2613 steps :
2714 - uses : actions/checkout@v4
2815
29- - name : Set up Python ${{ matrix.python-version }}
16+ - name : Set up Python
3017 uses : actions/setup-python@v5
3118 with :
32- python-version : ${{ matrix.python-version }}
33-
34- - name : Cache pip dependencies
35- uses : actions/cache@v4
36- with :
37- path : ~/.cache/pip
38- key : ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }}
39- restore-keys : |
40- ${{ runner.os }}-pip-
19+ python-version : ' 3.12'
20+ cache : ' pip'
21+ cache-dependency-path : ' pyproject.toml'
4122
4223 - name : Install dependencies
4324 run : |
4425 python -m pip install --upgrade pip
4526 pip install -e ".[dev]"
4627
47- - name : Run linting with ruff
48- if : matrix.python-version == '3.10'
28+ - name : Run linting
4929 run : |
5030 python -m ruff check .
5131 python -m ruff format --check .
5232
53- - name : Run type checking with mypy
54- run : python -m mypy --python-version=${{ matrix.python-version }} src tests
33+ - name : Run type checking
34+ run : python -m mypy src/
5535
56- - name : Run unit tests with pytest
57- run : python -m pytest tests/unit/ -v --cov=nutrient_dws --cov-report=xml --cov-report=term
58-
59- - name : Upload coverage to Codecov
60- uses : codecov/codecov-action@v5
61- with :
62- token : ${{ secrets.CODECOV_TOKEN }}
63- files : ./coverage.xml
64- flags : unittests
65- name : codecov-umbrella
66- fail_ci_if_error : false
36+ unit-tests :
37+ runs-on : ${{ matrix.os }}
38+ needs : lint-and-type-check
6739
68- integration-test :
69- runs-on : ubuntu-latest
70- # Run on: pushes to main/develop, PRs from same repo, and manual triggers
71- if : |
72- github.event_name == 'push' ||
73- github.event_name == 'workflow_dispatch' ||
74- (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository)
7540 strategy :
7641 matrix :
7742 python-version : ['3.10', '3.11', '3.12']
78-
43+ os : [ubuntu-latest, windows-latest, macos-latest]
44+
7945 steps :
8046 - uses : actions/checkout@v4
8147
8248 - name : Set up Python ${{ matrix.python-version }}
8349 uses : actions/setup-python@v5
8450 with :
8551 python-version : ${{ matrix.python-version }}
86-
87- - name : Cache pip dependencies
88- uses : actions/cache@v4
89- with :
90- path : ~/.cache/pip
91- key : ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }}
92- restore-keys : |
93- ${{ runner.os }}-pip-
52+ cache : ' pip'
53+ cache-dependency-path : ' pyproject.toml'
9454
9555 - name : Install dependencies
9656 run : |
9757 python -m pip install --upgrade pip
9858 pip install -e ".[dev]"
9959
100- - name : Check for API key availability
101- run : |
102- if [ -z "${{ secrets.NUTRIENT_DWS_API_KEY }}" ]; then
103- echo "::warning::NUTRIENT_DWS_API_KEY secret not found, skipping integration tests"
104- echo "skip_tests=true" >> $GITHUB_ENV
105-
106- # Provide context about why this might be happening
107- if [ "${{ github.event_name }}" == "pull_request" ]; then
108- if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then
109- echo "::notice::This appears to be a PR from a fork. Secrets are not available for security reasons."
110- else
111- echo "::error::This is a PR from the same repository but the API key is missing. Please check repository secrets configuration."
112- fi
113- else
114- echo "::error::Running on ${{ github.event_name }} event but API key is missing. Please configure NUTRIENT_DWS_API_KEY secret."
115- fi
116- else
117- echo "::notice::API key found, integration tests will run"
118- echo "skip_tests=false" >> $GITHUB_ENV
119- fi
120-
121- - name : Create integration config with API key
122- if : env.skip_tests != 'true'
123- run : |
124- python -c "
125- import os
126- with open('tests/integration/integration_config.py', 'w') as f:
127- f.write(f'API_KEY = \"{os.environ[\"NUTRIENT_DWS_API_KEY\"]}\"\n')
128- "
129- env :
130- NUTRIENT_DWS_API_KEY : ${{ secrets.NUTRIENT_DWS_API_KEY }}
131-
132- - name : Run integration tests
133- if : env.skip_tests != 'true'
134- run : python -m pytest tests/integration/ -v
135-
136- - name : Cleanup integration config
137- if : always()
138- run : rm -f tests/integration/integration_config.py
139-
140- # Provide feedback for fork PRs where integration tests can't run
141- integration-test-fork-feedback :
142- runs-on : ubuntu-latest
143- if : |
144- github.event_name == 'pull_request' &&
145- github.event.pull_request.head.repo.full_name != github.repository
146- steps :
147- - name : Comment on PR about integration tests
148- uses : actions/github-script@v7
60+ - name : Run unit tests with coverage
61+ run : python -m pytest tests/unit/ -v --cov=nutrient_dws --cov-report=xml --cov-report=term
62+
63+ - name : Upload coverage to Codecov
64+ uses : codecov/codecov-action@v4
65+ if : matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
14966 with :
150- github-token : ${{ secrets.GITHUB_TOKEN }}
151- script : |
152- const issue_number = context.issue.number;
153- const owner = context.repo.owner;
154- const repo = context.repo.repo;
155-
156- // Check if we've already commented
157- const comments = await github.rest.issues.listComments({
158- owner,
159- repo,
160- issue_number,
161- });
162-
163- const botComment = comments.data.find(comment =>
164- comment.user.type === 'Bot' &&
165- comment.body.includes('Integration tests are skipped for pull requests from forks')
166- );
167-
168- if (!botComment) {
169- await github.rest.issues.createComment({
170- owner,
171- repo,
172- issue_number,
173- body: `## Integration Tests Status\n\n` +
174- `Integration tests are skipped for pull requests from forks due to security restrictions. ` +
175- `These tests will run automatically after the PR is merged.\n\n` +
176- `**What this means:**\n` +
177- `- Unit tests, linting, and type checking have passed ✅\n` +
178- `- Integration tests require API credentials that aren't available to fork PRs\n` +
179- `- A maintainer will review your changes and merge if appropriate\n` +
180- `- Integration tests will run on the main branch after merge\n\n` +
181- `Thank you for your contribution! 🙏`
182- });
183- }
67+ token : ${{ secrets.CODECOV_TOKEN }}
68+ files : ./coverage.xml
69+ flags : unittests
70+ name : codecov-umbrella
18471
18572 build :
18673 runs-on : ubuntu-latest
187- needs : test
74+ needs : [lint-and-type-check, unit-tests]
18875
18976 steps :
19077 - uses : actions/checkout@v4
19380 uses : actions/setup-python@v5
19481 with :
19582 python-version : ' 3.12'
83+ cache : ' pip'
84+ cache-dependency-path : ' pyproject.toml'
19685
19786 - name : Install dependencies
19887 run : |
@@ -202,11 +91,18 @@ jobs:
20291 - name : Build package
20392 run : python -m build
20493
94+ - name : Verify build outputs
95+ run : |
96+ ls -la dist/
97+ test -f dist/*.whl
98+ test -f dist/*.tar.gz
99+
205100 - name : Check package with twine
206101 run : twine check dist/*
207102
208- - name : Upload artifacts
103+ - name : Upload build artifacts
209104 uses : actions/upload-artifact@v4
210105 with :
211- name : dist
106+ name : dist-${{ github.run_number }}
212107 path : dist/
108+ retention-days : 30
0 commit comments