Skip to content

Commit 63341d5

Browse files
author
Bruce Ashfield
committed
infra: add gitHub actions CI/CD with automated PyPI releases
This commit introduces automated testing and release workflows: CI Workflow (.github/workflows/ci.yml): - Runs tests on every push to master and all pull requests - Tests on Python 3.12 and 3.13 with fail-fast disabled - Includes pip caching for faster runs - Uploads test output artifacts on failure - Integrates with GitHub Actions UI via test wrapper script Release Workflow (.github/workflows/release.yml): - Triggers on git tags matching 'v*' pattern (e.g., v1.3.1) - Validates tag version matches lopper/VERSION file - Runs full test suite on Python 3.12 and 3.13 - Builds source distribution and wheel - Publishes to PyPI using trusted publisher (OIDC) - Supports optional environment protection for approval gate Test Wrapper (scripts/run_tests.sh): - Executes lopper_sanity.py --all - Parses [TEST PASSED] and [TEST FAILED] markers - Creates GitHub Actions error annotations for failures - Generates job summary with pass/fail statistics - Preserves original test exit code Release Process: 1. Update lopper/VERSION with new version 2. Commit and push to master 3. Create and push annotated tag (e.g., git tag -a v1.3.1) 4. GitHub Actions handles testing, validation, build, and PyPI publish Signed-off-by: Bruce Ashfield <bruce.ashfield@amd.com>
1 parent 62db895 commit 63341d5

3 files changed

Lines changed: 217 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Continuous Integration
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.12", "3.13"]
15+
fail-fast: false
16+
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Python ${{ matrix.python-version }}
22+
uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
cache: 'pip'
26+
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
pip install -r requirements.txt
31+
32+
- name: Run tests
33+
run: |
34+
chmod +x scripts/run_tests.sh
35+
./scripts/run_tests.sh
36+
37+
- name: Upload test output on failure
38+
if: failure()
39+
uses: actions/upload-artifact@v4
40+
with:
41+
name: test-output-py${{ matrix.python-version }}
42+
path: test_output.txt
43+
retention-days: 7

.github/workflows/release.yml

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
name: Release to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
matrix:
13+
python-version: ["3.12", "3.13"]
14+
fail-fast: false
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Python ${{ matrix.python-version }}
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
cache: 'pip'
25+
26+
- name: Install dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install -r requirements.txt
30+
31+
- name: Run tests
32+
run: |
33+
chmod +x scripts/run_tests.sh
34+
./scripts/run_tests.sh
35+
36+
- name: Upload test output on failure
37+
if: failure()
38+
uses: actions/upload-artifact@v4
39+
with:
40+
name: test-output-py${{ matrix.python-version }}
41+
path: test_output.txt
42+
retention-days: 7
43+
44+
validate-version:
45+
runs-on: ubuntu-latest
46+
outputs:
47+
version: ${{ steps.get-version.outputs.version }}
48+
49+
steps:
50+
- name: Checkout code
51+
uses: actions/checkout@v4
52+
53+
- name: Extract and validate version
54+
id: get-version
55+
run: |
56+
# Extract version from tag (strip 'v' prefix)
57+
TAG_VERSION=${GITHUB_REF#refs/tags/v}
58+
echo "Tag version: $TAG_VERSION"
59+
60+
# Read version from VERSION file
61+
FILE_VERSION=$(cat lopper/VERSION | tr -d '[:space:]')
62+
echo "VERSION file: $FILE_VERSION"
63+
64+
# Compare versions
65+
if [ "$TAG_VERSION" != "$FILE_VERSION" ]; then
66+
echo "::error::Tag version ($TAG_VERSION) does not match VERSION file ($FILE_VERSION)"
67+
echo "Please update lopper/VERSION to match the tag version before releasing."
68+
exit 1
69+
fi
70+
71+
echo "Version validation passed: $TAG_VERSION"
72+
echo "version=$TAG_VERSION" >> $GITHUB_OUTPUT
73+
74+
build:
75+
needs: [test, validate-version]
76+
runs-on: ubuntu-latest
77+
78+
steps:
79+
- name: Checkout code
80+
uses: actions/checkout@v4
81+
82+
- name: Set up Python
83+
uses: actions/setup-python@v5
84+
with:
85+
python-version: "3.12"
86+
87+
- name: Install build tools
88+
run: |
89+
python -m pip install --upgrade pip
90+
pip install build
91+
92+
- name: Build distributions
93+
run: python -m build
94+
95+
- name: Upload artifacts
96+
uses: actions/upload-artifact@v4
97+
with:
98+
name: dist-${{ needs.validate-version.outputs.version }}
99+
path: dist/
100+
retention-days: 30
101+
102+
publish:
103+
needs: build
104+
runs-on: ubuntu-latest
105+
environment: release
106+
permissions:
107+
id-token: write
108+
contents: read
109+
110+
steps:
111+
- name: Download build artifacts
112+
uses: actions/download-artifact@v4
113+
with:
114+
pattern: dist-*
115+
merge-multiple: true
116+
path: dist/
117+
118+
- name: Publish to PyPI
119+
uses: pypa/gh-action-pypi-publish@release/v1

scripts/run_tests.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
echo "Running lopper_sanity.py test suite..."
5+
echo "========================================="
6+
7+
# Run tests and capture output
8+
python3 lopper_sanity.py --all 2>&1 | tee test_output.txt
9+
TEST_EXIT_CODE=${PIPESTATUS[0]}
10+
11+
echo ""
12+
echo "========================================="
13+
echo "Analyzing test results..."
14+
15+
# Parse test results
16+
PASSED=$(grep -c "\[TEST PASSED\]:" test_output.txt || true)
17+
FAILED=$(grep -c "\[TEST FAILED\]:" test_output.txt || true)
18+
19+
echo "Tests passed: $PASSED"
20+
echo "Tests failed: $FAILED"
21+
22+
# Create GitHub Actions annotations for failures (only in CI environment)
23+
if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then
24+
echo "Creating GitHub Actions annotations..."
25+
26+
while IFS= read -r line; do
27+
if [[ $line =~ \[TEST\ FAILED\]:\ (.+) ]]; then
28+
echo "::error::Test Failed: ${BASH_REMATCH[1]}"
29+
fi
30+
done < test_output.txt
31+
32+
# Generate job summary
33+
echo "## Test Results" >> $GITHUB_STEP_SUMMARY
34+
echo "" >> $GITHUB_STEP_SUMMARY
35+
echo "- ✅ Passed: $PASSED" >> $GITHUB_STEP_SUMMARY
36+
echo "- ❌ Failed: $FAILED" >> $GITHUB_STEP_SUMMARY
37+
echo "" >> $GITHUB_STEP_SUMMARY
38+
39+
if [ $FAILED -gt 0 ]; then
40+
echo "### Failed Tests" >> $GITHUB_STEP_SUMMARY
41+
echo "" >> $GITHUB_STEP_SUMMARY
42+
grep "\[TEST FAILED\]:" test_output.txt | while read -r line; do
43+
echo "- $line" >> $GITHUB_STEP_SUMMARY
44+
done
45+
fi
46+
fi
47+
48+
# Exit with the test suite's exit code
49+
if [ $TEST_EXIT_CODE -eq 0 ]; then
50+
echo "✅ All tests passed!"
51+
else
52+
echo "❌ Tests failed with exit code $TEST_EXIT_CODE"
53+
fi
54+
55+
exit $TEST_EXIT_CODE

0 commit comments

Comments
 (0)