@@ -2,119 +2,245 @@ name: "dag-pypergraph"
22
33on :
44 push :
5- branches : [ " master", " dev" ]
5+ branches : [master, dev]
66 pull_request :
7- branches : [ "master", "dev" ]
7+ branches : [master, dev]
8+
9+ env :
10+ PYTHON_DEFAULT_VERSION : " 3.11"
811
912jobs :
10- build :
13+ test :
14+ name : Test (Python ${{ matrix.python-version }})
1115 runs-on : ubuntu-latest
1216 strategy :
1317 fail-fast : false
1418 matrix :
1519 python-version : ["3.9", "3.10", "3.11", "3.12", "3.13"]
1620
1721 steps :
18- - uses : actions/checkout@v4
19- - name : Set up Python ${{ matrix.python-version }}
20- uses : actions/setup-python@v3
21- with :
22- python-version : ${{ matrix.python-version }}
23- - name : Install dependencies
24- run : |
25- python -m pip install --upgrade pip
26- python -m pip install ruff pytest pytest-asyncio pytest-httpx
27- if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
28- - name : Lint with Ruff
29- run : |
30- ruff format .
31- ruff check . --fix --output-format=github --target-version=py39
32- - name : Test with pytest
33- run : |
34- pytest -q -m "not integration" --verbose
35- - name : Install Bandit
36- run : pip install bandit
37-
38- - name : Run Bandit security check
39- run : |
40- bandit -r . --exclude "tests/,test_*.py" | tee security_report.txt
41- if grep -E "Severity: (Medium|High)" security_report.txt; then
42- echo "Security issues detected with Medium or High severity"
43- exit 1
44- fi
45-
46- - name : Security check report artifacts
47- uses : actions/upload-artifact@v4
48- with :
49- name : security-report-${{ github.run_id }}-${{ matrix.python-version }}
50- path : security_report.txt
51-
52- hatchling :
22+ - name : Checkout code
23+ uses : actions/checkout@v4
24+ with :
25+ fetch-depth : 0
26+ ref : " ${{ needs.version-and-tag.outputs.new_version }}"
27+
28+ - name : Set up Python ${{ matrix.python-version }}
29+ uses : actions/setup-python@v5
30+ with :
31+ python-version : ${{ matrix.python-version }}
32+ cache : ' pip'
33+
34+ - name : Install dependencies
35+ run : |
36+ python -m pip install --upgrade pip
37+ python -m pip install ruff pytest pytest-asyncio bandit hatch pytest-httpx
38+ if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
39+ # Install project in editable mode for testing
40+ pip install -e . || echo "Editable install failed, continuing with regular install"
41+
42+ - name : Lint with Ruff
43+ run : |
44+ # Auto-fix what can be automatically fixed
45+ ruff check . --fix --exit-zero
46+ # Check for remaining issues
47+ ruff check . --output-format=github
48+ # Check formatting
49+ ruff format --check .
50+
51+ - name : Security check with Bandit
52+ run : |
53+ bandit -r . -f json -o bandit-report.json --exclude "tests/,test_*.py" || true
54+ bandit -r . --exclude "tests/,test_*.py" --severity-level medium --exit-zero
55+
56+ - name : Run tests
57+ run : pytest -q -m "not integration" --verbose
58+
59+ - name : Upload security report
60+ if : always()
61+ uses : actions/upload-artifact@v4
62+ with :
63+ name : security-report-py${{ matrix.python-version }}
64+ path : bandit-report.json
65+ retention-days : 30
66+
67+ version-and-tag :
68+ name : Version and tag
5369 runs-on : ubuntu-latest
54- needs : build
70+ needs : [ test ]
71+ if : github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev')
72+ permissions :
73+ contents : write
74+ outputs :
75+ version : ${{ steps.version.outputs.version }}
76+ new_version : ${{ steps.version.outputs.new_version }}
77+ version_changed : ${{ steps.version.outputs.version_changed }}
5578 steps :
56- - uses : actions/checkout@v4
79+ - name : Checkout code
80+ uses : actions/checkout@v4
81+ with :
82+ fetch-depth : 0
83+ token : ${{ secrets.GITHUB_TOKEN }}
84+
5785 - name : Set up Python
58- uses : actions/setup-python@v3
86+ uses : actions/setup-python@v5
5987 with :
60- python-version : ' 3.11'
61- - name : Install Hatch
88+ python-version : ${{ env.PYTHON_DEFAULT_VERSION }}
89+
90+ - name : Install dependencies
6291 run : pip install hatch
92+
93+ - name : Configure git
94+ run : |
95+ git config --global user.name 'github-actions[bot]'
96+ git config --global user.email 'github-actions[bot]@users.noreply.github.com'
97+
98+ - name : Get version and tag
99+ id : version
100+ run : |
101+ # Get current version from project (via Hatch)
102+ CURRENT_VERSION=$(hatch version)
103+ echo "Current version: $CURRENT_VERSION"
104+
105+ # Output version variables
106+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
107+ echo "new_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
108+
109+ # Check if tag already exists
110+ if git rev-parse "v$CURRENT_VERSION" >/dev/null 2>&1; then
111+ echo "Tag v$CURRENT_VERSION already exists. Skipping."
112+ echo "version_changed=false" >> $GITHUB_OUTPUT
113+ else
114+ echo "version_changed=true" >> $GITHUB_OUTPUT
115+ # Create and push new tag
116+ git tag "v$CURRENT_VERSION"
117+ git push origin "v$CURRENT_VERSION"
118+ fi
119+
120+ build :
121+ name : Build package
122+ runs-on : ubuntu-latest
123+ needs : [test, version-and-tag]
124+ if : needs.version-and-tag.outputs.version_changed == 'true'
125+ steps :
126+ - name : Checkout code
127+ uses : actions/checkout@v4
128+
129+ - name : Set up Python
130+ uses : actions/setup-python@v5
131+ with :
132+ python-version : ${{ env.PYTHON_DEFAULT_VERSION }}
133+ cache : ' pip'
134+
135+ - name : Install build tools
136+ run : |
137+ python -m pip install --upgrade pip
138+ python -m pip install hatch build
139+ # Debug: Check pyproject.toml structure
140+ echo "=== pyproject.toml content ==="
141+ cat pyproject.toml
142+ echo "=== Current version ==="
143+ hatch version || echo "hatch version failed"
144+
63145 - name : Build package
64- run : hatch build
65- - name : Upload package artifacts
146+ run : |
147+ # Try hatch build first, fallback to python -m build
148+ hatch build || python -m build
149+
150+ - name : Upload build artifacts
66151 uses : actions/upload-artifact@v4
67152 with :
68- name : package-build
153+ name : package-dist
69154 path : dist/
155+ retention-days : 7
70156
71157 release :
72- needs : hatchling
158+ name : Create GitHub release
73159 runs-on : ubuntu-latest
74- if : contains('refs/heads/dev refs/heads/master', github.ref)
160+ needs : [version-and-tag, build]
161+ if : needs.version-and-tag.outputs.version_changed == 'true'
162+ permissions :
163+ contents : write
75164 steps :
76- - uses : actions/checkout@v4
77- - name : Set up Python
78- uses : actions/setup-python@v3
165+ - name : Checkout code
166+ uses : actions/checkout@v4
79167 with :
80- python-version : ' 3.11'
81- - name : Extract version from pyproject.toml
82- id : get_version
83- run : echo "VERSION=$(sed -n 's/version = \"\(.*\)\"/\1/p' pyproject.toml)" >> $GITHUB_ENV
84- - name : Download package artifacts
168+ fetch-depth : 0
169+
170+ - name : Download build artifacts
85171 uses : actions/download-artifact@v4
86172 with :
87- name : package-build
173+ name : package-dist
88174 path : ./dist
175+
176+ - name : List dist contents
177+ run : |
178+ echo "=== Contents of dist directory ==="
179+ ls -la ./dist/ || echo "dist directory is empty or doesn't exist"
180+
89181 - name : Create GitHub Release
90- uses : softprops/action-gh-release@v2
182+ uses : softprops/action-gh-release@v1
91183 with :
92- tag_name : " v${{ env.VERSION }}"
93- name : " Pypergraph v${{ env.VERSION }}"
184+ tag_name : " v${{ needs.version-and-tag.outputs.new_version }}"
185+ name : " Pypergraph v${{ needs.version-and-tag.outputs.new_version }}"
94186 draft : false
95- prerelease : ${{ github.ref == 'refs/heads/dev' }} # Pre-release if on branch 'dev'
187+ prerelease : ${{ contains(needs.version-and-tag.outputs.new_version, 'b') || github.ref == 'refs/heads/dev' }}
96188 files : ./dist/*
189+ generate_release_notes : true
97190 env :
98- GITHUB_TOKEN : ${{ secrets.GH_PYPERGRAPH_PAT }}
191+ GITHUB_TOKEN : ${{ secrets.GITHUB_TOKEN }}
99192
100- publish :
101- needs : [ hatchling, build ] # Only runs if tests pass
193+ publish-pypi :
194+ name : Publish to PyPI
102195 runs-on : ubuntu-latest
103- if : github.ref == 'refs/heads/master'
196+ needs : [version-and-tag, build]
197+ if : needs.version-and-tag.outputs.version_changed == 'true' && github.ref == 'refs/heads/master'
198+ environment :
199+ name : pypi
200+ url : https://pypi.org/p/pypergraph-dag
201+ permissions :
202+ id-token : write # For trusted publishing
104203 steps :
105- - uses : actions/checkout@v4
106- - name : Set up Python
107- uses : actions/setup-python@v3
108- with :
109- python-version : ' 3.11'
110- - name : Download package artifacts
204+ - name : Download build artifacts
111205 uses : actions/download-artifact@v4
112206 with :
113- name : package-build
207+ name : package-dist
114208 path : ./dist
115- - name : Install Twine
116- run : pip install twine
209+
210+ - name : List dist contents
211+ run : |
212+ echo "=== Contents of dist directory ==="
213+ ls -la ./dist/ || echo "dist directory is empty or doesn't exist"
214+
117215 - name : Publish to PyPI
118- env :
119- PYPI_PYPERGRAPH_TOKEN : ${{ secrets.PYPI_PYPERGRAPH_TOKEN }}
120- run : twine upload dist/* -u __token__ -p "$PYPI_PYPERGRAPH_TOKEN"
216+ uses : pypa/gh-action-pypi-publish@release/v1
217+ with :
218+ verbose : true
219+
220+ publish-test-pypi :
221+ name : Publish to Test PyPI
222+ runs-on : ubuntu-latest
223+ needs : [version-and-tag, build]
224+ if : needs.version-and-tag.outputs.version_changed == 'true' && github.ref == 'refs/heads/dev'
225+ environment :
226+ name : test-pypi
227+ url : https://test.pypi.org/p/pypergraph-dag
228+ permissions :
229+ id-token : write # For trusted publishing
230+ steps :
231+ - name : Download build artifacts
232+ uses : actions/download-artifact@v4
233+ with :
234+ name : package-dist
235+ path : ./dist
236+
237+ - name : List dist contents
238+ run : |
239+ echo "=== Contents of dist directory ==="
240+ ls -la ./dist/ || echo "dist directory is empty or doesn't exist"
241+
242+ - name : Publish to Test PyPI
243+ uses : pypa/gh-action-pypi-publish@release/v1
244+ with :
245+ repository-url : https://test.pypi.org/legacy/
246+ verbose : true
0 commit comments