Skip to content

Commit ba2c259

Browse files
committed
streamline to ci.yml
1 parent 08ed455 commit ba2c259

File tree

4 files changed

+284
-0
lines changed

4 files changed

+284
-0
lines changed
File renamed without changes.
File renamed without changes.

.github/workflows/ci.yml

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- dev
8+
pull_request:
9+
branches:
10+
- main
11+
- dev
12+
13+
jobs:
14+
lint:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python 3.11
20+
uses: actions/setup-python@v3
21+
with:
22+
python-version: "3.11"
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install .[dev]
28+
29+
- name: pylint analysis and custom pylint HTML report
30+
run: |
31+
# Create a timestamp for the output file
32+
TIMESTAMP=$(date +"%Y%m%d_%H%M%S_%Z")
33+
PYLINT_OUTPUT_FILE="pylint_output_${TIMESTAMP}.txt"
34+
35+
# Generate text report
36+
pylint src/rattlesnake/**/*.py --output-format=text --reports=yes > "$PYLINT_OUTPUT_FILE" || true
37+
38+
# Extract the score from pylint output
39+
SCORE=$(grep "Your code has been rated at" "$PYLINT_OUTPUT_FILE" | awk '{print $7}' | cut -d'/' -f1 || echo "0")
40+
41+
# Output the score and file name
42+
echo "🏁 Pylint analysis completed. 🏁"
43+
echo " 📄 Output file: $PYLINT_OUTPUT_FILE"
44+
echo " 🏆 Pylint score: $SCORE out of 10"
45+
46+
# Read the pylint output and create a custom HTML report.
47+
python src/rattlesnake/cicd/report_pylint.py \
48+
--input_file "$PYLINT_OUTPUT_FILE" \
49+
--output_file pylint_report.html \
50+
--timestamp "$TIMESTAMP" \
51+
--run_id ${{ github.run_id }} \
52+
--ref_name ${{ github.ref_name }} \
53+
--github_sha ${{ github.sha }} \
54+
--github_repo ${{ github.repository }}
55+
56+
- name: Upload pylint artifact
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: pylint-report
60+
path: |
61+
pylint_report.html
62+
pylint_output_*.txt
63+
64+
coverage:
65+
runs-on: ${{ matrix.os }}
66+
strategy:
67+
matrix:
68+
os: [macos-latest]
69+
python-version: ["3.11"]
70+
71+
steps:
72+
- name: Checkout code
73+
uses: actions/checkout@v4
74+
75+
- name: Set up Python ${{ matrix.python-version }}
76+
uses: actions/setup-python@v3
77+
with:
78+
python-version: ${{ matrix.python-version }}
79+
80+
- name: Install dependencies
81+
run: |
82+
python -m pip install --upgrade pip
83+
pip install .[dev]
84+
85+
- name: pytest coverage and custom coverage HTML report
86+
run: |
87+
# Create a timestamp for the output file
88+
TIMESTAMP=$(date +"%Y%m%d_%H%M%S_%Z")
89+
90+
pytest -v --cov=rattlesnake --cov-report=html --cov-report=xml --cov-report=term-missing
91+
92+
python src/rattlesnake/cicd/report_pytest.py \
93+
--input_file coverage.xml \
94+
--output_file pytest_report.html \
95+
--timestamp "$TIMESTAMP" \
96+
--run_id ${{ github.run_id }} \
97+
--ref_name ${{ github.ref_name }} \
98+
--github_sha ${{ github.sha }} \
99+
--github_repo ${{ github.repository }}
100+
101+
- name: Upload pytest artifact
102+
uses: actions/upload-artifact@v4
103+
with:
104+
name: pytest-report
105+
path: |
106+
pytest_report.html
107+
htmlcov/
108+
coverage.xml
109+
110+
deploy:
111+
needs: [lint, coverage]
112+
runs-on: ubuntu-latest
113+
if: github.ref == 'refs/heads/dev'
114+
115+
steps:
116+
- name: Checkout code
117+
uses: actions/checkout@v4
118+
119+
- name: Set up Python 3.11
120+
uses: actions/setup-python@v3
121+
with:
122+
python-version: "3.11"
123+
124+
- name: Install dependencies
125+
run: |
126+
python -m pip install --upgrade pip
127+
pip install .[dev]
128+
129+
- name: Download pylint artifact
130+
uses: actions/download-artifact@v4
131+
with:
132+
name: pylint-report
133+
path: pylint-report
134+
135+
- name: Download pytest artifact
136+
uses: actions/download-artifact@v4
137+
with:
138+
name: pytest-report
139+
path: pytest-report
140+
141+
- name: Create badges
142+
run: |
143+
# Pylint badge
144+
PYLINT_OUTPUT_FILE=$(ls pylint-report/pylint_output_*.txt)
145+
SCORE=$(grep "Your code has been rated at" "$PYLINT_OUTPUT_FILE" | awk '{print $7}' | cut -d'/' -f1 || echo "0")
146+
python3 src/rattlesnake/cicd/pylint_badge_color.py "$SCORE"
147+
mkdir -p badges
148+
curl -o badges/pylint.svg "https://img.shields.io/badge/pylint-${SCORE}-${{ env.BADGE_COLOR }}.svg"
149+
cat > badges/pylint-info.json << EOF
150+
{
151+
"score": "${SCORE}",
152+
"color": "${{ env.BADGE_COLOR }}",
153+
"pages_url": "https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/reports/pylint/",
154+
"workflow_url": "${{ github.server_url }}/${{ github.repository }}/actions/workflows/ci.yml",
155+
"run_id": "${{ github.run_id }}",
156+
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
157+
}
158+
EOF
159+
160+
# Pytest badge
161+
python3 src/rattlesnake/cicd/coverage_badge_color.py pytest-report/coverage.xml
162+
163+
REPO_URL="${{ github.server_url }}/${{ github.repository }}"
164+
WORKFLOW_URL="${REPO_URL}/actions/workflows/ci.yml"
165+
curl -o badges/coverage.svg "https://img.shields.io/badge/coverage-${{ env.COVERAGE }}%25-${{ env.BADGE_COLOR_COV }}.svg"
166+
cat > badges/coverage-info.json << EOF
167+
{
168+
"coverage": "${{ env.COVERAGE }}",
169+
"color": "${{ env.BADGE_COLOR_COV }}",
170+
"pages_url": "https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/reports/coverage/",
171+
"workflow_url": "${WORKFLOW_URL}",
172+
"run_id": "${{ github.run_id }}",
173+
"artifact_url": "${REPO_URL}/actions/runs/${{ github.run_id }}",
174+
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
175+
}
176+
EOF
177+
178+
- name: Commit badges
179+
run: |
180+
git pull
181+
git config --local user.email "[email protected]"
182+
git config --local user.name "GitHub Action"
183+
git add badges/
184+
git diff --staged --quiet || git commit -m "Update badges [skip ci]"
185+
git push
186+
env:
187+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
188+
189+
- name: Assemble pages directory
190+
run: |
191+
mkdir -p pages/reports/pylint
192+
mkdir -p pages/reports/coverage
193+
mv pylint-report/pylint_report.html pages/reports/pylint/index.html
194+
mv pytest-report/pytest_report.html pages/reports/coverage/index.html
195+
mv pytest-report/htmlcov pages/reports/coverage/
196+
cat > pages/index.html << 'EOF'
197+
<!DOCTYPE html>
198+
<html lang="en">
199+
<head>
200+
<meta charset="UTF-8">
201+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
202+
<title>Rattlesnake Vibration Controller - Reports</title>
203+
<style>
204+
body { font-family: sans-serif; margin: 40px; background-color: #f6f8fa; }
205+
.container { max-width: 800px; margin: 0 auto; background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
206+
h1 { color: #333; }
207+
a { color: #0366d6; text-decoration: none; }
208+
a:hover { text-decoration: underline; }
209+
ul { list-style-type: none; padding: 0; }
210+
li { background-color: #f1f1f1; margin: 10px 0; padding: 15px; border-radius: 4px; }
211+
</style>
212+
</head>
213+
<body>
214+
<div class="container">
215+
<h1>Rattlesnake Vibration Controller - Reports</h1>
216+
<p>This page provides links to the latest reports generated by the CI/CD pipeline.</p>
217+
<ul>
218+
<li><a href="reports/coverage/index.html">View Pytest Coverage Report</a></li>
219+
<li><a href="reports/pylint/index.html">View Pylint Report</a></li>
220+
</ul>
221+
</div>
222+
</body>
223+
</html>
224+
EOF
225+
226+
- name: Deploy to GitHub Pages
227+
uses: peaceiris/actions-gh-pages@v3
228+
with:
229+
github_token: ${{ secrets.GITHUB_TOKEN }}
230+
publish_dir: ./pages
231+
publish_branch: gh-pages
232+
keep_files: true
233+
commit_message: 'Deploy reports for ${{ github.sha }}'
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Calculates the coverage percentage from a coverage.xml file and determines the
3+
color for a badge. It then writes the coverage and color to the GitHub
4+
environment file.
5+
"""
6+
7+
import xml.etree.ElementTree as ET
8+
import os
9+
import sys
10+
11+
12+
def get_coverage_and_color(coverage_file: str):
13+
"""
14+
Parses a coverage.xml file to get the coverage percentage and determines
15+
the badge color.
16+
17+
Args:
18+
coverage_file: The path to the coverage.xml file.
19+
"""
20+
try:
21+
tree = ET.parse(coverage_file)
22+
root = tree.getroot()
23+
coverage = float(root.attrib["line-rate"]) * 100
24+
except (FileNotFoundError, KeyError, ET.ParseError, IndexError):
25+
coverage = 0.0
26+
27+
print(f"Coverage: {coverage:.1f}%")
28+
29+
if coverage >= 90:
30+
color = "brightgreen"
31+
elif coverage >= 80:
32+
color = "green"
33+
elif coverage >= 70:
34+
color = "yellow"
35+
elif coverage >= 60:
36+
color = "orange"
37+
else:
38+
color = "red"
39+
40+
if "GITHUB_ENV" in os.environ:
41+
with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as f:
42+
f.write(f"COVERAGE={coverage:.1f}\n")
43+
f.write(f"BADGE_COLOR_COV={color}\n")
44+
45+
46+
if __name__ == "__main__":
47+
if len(sys.argv) > 1:
48+
get_coverage_and_color(sys.argv[1])
49+
else:
50+
print("Usage: python coverage_badge_color.py <path_to_coverage.xml>")
51+
sys.exit(1)

0 commit comments

Comments
 (0)