Skip to content

Commit b5210cc

Browse files
committed
Add automated security update workflows
New workflows: - security-updates.yml: Weekly automated security scanning and PR creation - dependabot-auto-merge.yml: Auto-merge Dependabot PRs after tests pass - dependabot.yml: Configure Dependabot for Python and GitHub Actions Features: - Scans dependencies with pip-audit and safety - Automatically creates PRs with security fixes - Runs full test suite before merging - Auto-bumps version for security releases - Creates GitHub releases automatically - Publishes to PyPI via existing workflow Schedule: - Runs every Monday at 2 AM UTC - Can be triggered manually - Responds to Dependabot alerts immediately
1 parent 01325de commit b5210cc

4 files changed

Lines changed: 353 additions & 1 deletion

File tree

.github/dependabot.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
version: 2
2+
updates:
3+
# Enable version updates for Python dependencies
4+
- package-ecosystem: "pip"
5+
directory: "/"
6+
schedule:
7+
interval: "weekly"
8+
day: "monday"
9+
time: "02:00"
10+
open-pull-requests-limit: 5
11+
reviewers:
12+
- "PigeonSec"
13+
labels:
14+
- "dependencies"
15+
- "automated"
16+
commit-message:
17+
prefix: "deps"
18+
include: "scope"
19+
# Group minor and patch updates together
20+
groups:
21+
production-dependencies:
22+
patterns:
23+
- "*"
24+
update-types:
25+
- "minor"
26+
- "patch"
27+
28+
# Enable security updates for GitHub Actions
29+
- package-ecosystem: "github-actions"
30+
directory: "/"
31+
schedule:
32+
interval: "weekly"
33+
open-pull-requests-limit: 3
34+
labels:
35+
- "dependencies"
36+
- "github-actions"
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
name: Dependabot Auto-Merge
2+
3+
on:
4+
pull_request:
5+
branches: [ master ]
6+
7+
permissions:
8+
contents: write
9+
pull-requests: write
10+
11+
jobs:
12+
dependabot-auto-merge:
13+
runs-on: ubuntu-latest
14+
if: github.actor == 'dependabot[bot]'
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: '3.12'
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -e .
28+
pip install pytest
29+
30+
- name: Run unit tests
31+
id: unit_tests
32+
run: |
33+
pytest tests/test_validator.py -v --tb=short
34+
echo "unit_tests_passed=true" >> $GITHUB_OUTPUT
35+
36+
- name: Run CLI tests
37+
id: cli_tests
38+
run: |
39+
pytest tests/test_cli.py -v --tb=short
40+
echo "cli_tests_passed=true" >> $GITHUB_OUTPUT
41+
42+
- name: Check if security update
43+
id: check_security
44+
env:
45+
GH_TOKEN: ${{ github.token }}
46+
run: |
47+
PR_BODY=$(gh pr view ${{ github.event.pull_request.number }} --json body --jq .body)
48+
49+
if echo "$PR_BODY" | grep -iq "security"; then
50+
echo "is_security_update=true" >> $GITHUB_OUTPUT
51+
echo "🔒 This is a security update"
52+
else
53+
echo "is_security_update=false" >> $GITHUB_OUTPUT
54+
fi
55+
56+
- name: Auto-approve and merge
57+
if: steps.unit_tests.outputs.unit_tests_passed == 'true' && steps.cli_tests.outputs.cli_tests_passed == 'true'
58+
env:
59+
GH_TOKEN: ${{ github.token }}
60+
run: |
61+
# Approve the PR
62+
gh pr review ${{ github.event.pull_request.number }} --approve --body "✅ All tests passed. Auto-approving Dependabot PR."
63+
64+
# Enable auto-merge
65+
gh pr merge ${{ github.event.pull_request.number }} --auto --squash --delete-branch
66+
67+
echo "✅ Auto-merge enabled for Dependabot PR #${{ github.event.pull_request.number }}"
68+
69+
- name: Create release if security update
70+
if: |
71+
steps.unit_tests.outputs.unit_tests_passed == 'true' &&
72+
steps.cli_tests.outputs.cli_tests_passed == 'true' &&
73+
steps.check_security.outputs.is_security_update == 'true'
74+
env:
75+
GH_TOKEN: ${{ github.token }}
76+
run: |
77+
# Wait for PR to merge
78+
sleep 10
79+
80+
# Get current version
81+
VERSION=$(python -c "import re; content=open('pyresolvers/lib/core/__version__.py').read(); print(re.search(r\"'([^']+)'\", content).group(1))")
82+
83+
# Calculate new patch version
84+
IFS='.' read -ra PARTS <<< "$VERSION"
85+
MAJOR="${PARTS[0]}"
86+
MINOR="${PARTS[1]}"
87+
PATCH="${PARTS[2]}"
88+
NEW_PATCH=$((PATCH + 1))
89+
NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH"
90+
91+
# Update version file
92+
echo "__version__ = '$NEW_VERSION'" > pyresolvers/lib/core/__version__.py
93+
94+
# Commit and push
95+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
96+
git config --local user.name "github-actions[bot]"
97+
git add pyresolvers/lib/core/__version__.py
98+
git commit -m "Bump version to $NEW_VERSION for security update"
99+
git push
100+
101+
# Create tag
102+
git tag -a "v$NEW_VERSION" -m "Security update v$NEW_VERSION"
103+
git push --tags
104+
105+
# Create GitHub release (this will trigger PyPI publish)
106+
gh release create "v$NEW_VERSION" \
107+
--title "v$NEW_VERSION - Security Update" \
108+
--notes "## Security Update
109+
110+
This release includes security dependency updates from Dependabot.
111+
112+
### Changes
113+
- Security dependency updates
114+
- All tests passed ✅
115+
116+
### Automated Release
117+
This release was automatically created after Dependabot's security updates passed all tests.
118+
119+
See PR #${{ github.event.pull_request.number }} for details."
120+
121+
echo "✅ Created release v$NEW_VERSION"
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
name: Security Updates
2+
3+
on:
4+
schedule:
5+
# Run every Monday at 2 AM UTC
6+
- cron: '0 2 * * 1'
7+
workflow_dispatch: # Allow manual trigger
8+
9+
permissions:
10+
contents: write
11+
pull-requests: write
12+
13+
jobs:
14+
security-scan:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: '3.12'
26+
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
pip install -e .
31+
pip install pytest pip-audit safety
32+
33+
- name: Run security audit with pip-audit
34+
id: pip_audit
35+
continue-on-error: true
36+
run: |
37+
pip-audit --desc > security-report.txt 2>&1 || echo "vulnerabilities_found=true" >> $GITHUB_OUTPUT
38+
cat security-report.txt
39+
40+
- name: Check for vulnerabilities with Safety
41+
id: safety_check
42+
continue-on-error: true
43+
run: |
44+
safety check --json > safety-report.json 2>&1 || echo "safety_issues=true" >> $GITHUB_OUTPUT
45+
46+
- name: Attempt to fix vulnerabilities
47+
if: steps.pip_audit.outputs.vulnerabilities_found == 'true' || steps.safety_check.outputs.safety_issues == 'true'
48+
id: fix_vulnerabilities
49+
run: |
50+
# Upgrade all dependencies to latest secure versions
51+
pip list --outdated --format=json | python -c "
52+
import json, sys
53+
packages = json.load(sys.stdin)
54+
for pkg in packages:
55+
print(pkg['name'])
56+
" > outdated.txt
57+
58+
# Read requirements and upgrade
59+
if [ -f "requirements.txt" ]; then
60+
while IFS= read -r package; do
61+
if [ ! -z "$package" ]; then
62+
pip install --upgrade "$package" || true
63+
fi
64+
done < outdated.txt
65+
66+
# Freeze new versions
67+
pip freeze | grep -v "pyresolvers" > requirements-new.txt
68+
mv requirements-new.txt requirements.txt
69+
70+
echo "updated=true" >> $GITHUB_OUTPUT
71+
fi
72+
73+
- name: Run all tests
74+
if: steps.fix_vulnerabilities.outputs.updated == 'true'
75+
id: run_tests
76+
run: |
77+
pytest tests/test_validator.py -v --tb=short
78+
pytest tests/test_cli.py -v --tb=short
79+
echo "tests_passed=true" >> $GITHUB_OUTPUT
80+
81+
- name: Get current version
82+
if: steps.run_tests.outputs.tests_passed == 'true'
83+
id: get_version
84+
run: |
85+
VERSION=$(python -c "import re; content=open('pyresolvers/lib/core/__version__.py').read(); print(re.search(r\"'([^']+)'\", content).group(1))")
86+
echo "current_version=$VERSION" >> $GITHUB_OUTPUT
87+
88+
# Calculate new patch version
89+
IFS='.' read -ra PARTS <<< "$VERSION"
90+
MAJOR="${PARTS[0]}"
91+
MINOR="${PARTS[1]}"
92+
PATCH="${PARTS[2]}"
93+
NEW_PATCH=$((PATCH + 1))
94+
NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH"
95+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
96+
97+
- name: Bump version
98+
if: steps.run_tests.outputs.tests_passed == 'true'
99+
run: |
100+
NEW_VERSION="${{ steps.get_version.outputs.new_version }}"
101+
echo "__version__ = '$NEW_VERSION'" > pyresolvers/lib/core/__version__.py
102+
103+
- name: Create security update branch
104+
if: steps.run_tests.outputs.tests_passed == 'true'
105+
run: |
106+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
107+
git config --local user.name "github-actions[bot]"
108+
109+
BRANCH_NAME="security/auto-update-$(date +%Y%m%d)"
110+
git checkout -b $BRANCH_NAME
111+
112+
git add requirements.txt pyresolvers/lib/core/__version__.py
113+
114+
# Create detailed commit message
115+
echo "Security update v${{ steps.get_version.outputs.new_version }}" > commit-msg.txt
116+
echo "" >> commit-msg.txt
117+
echo "Automated security dependency updates:" >> commit-msg.txt
118+
echo "" >> commit-msg.txt
119+
cat security-report.txt >> commit-msg.txt || echo "No detailed report available" >> commit-msg.txt
120+
echo "" >> commit-msg.txt
121+
echo "All tests passed after updates." >> commit-msg.txt
122+
123+
git commit -F commit-msg.txt
124+
git push origin $BRANCH_NAME
125+
126+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_ENV
127+
128+
- name: Create Pull Request
129+
if: steps.run_tests.outputs.tests_passed == 'true'
130+
env:
131+
GH_TOKEN: ${{ github.token }}
132+
run: |
133+
BRANCH_NAME="security/auto-update-$(date +%Y%m%d)"
134+
NEW_VERSION="${{ steps.get_version.outputs.new_version }}"
135+
136+
gh pr create \
137+
--title "🔒 Security Update v$NEW_VERSION" \
138+
--body "$(cat <<'EOF'
139+
## Automated Security Update
140+
141+
This PR contains automated security dependency updates.
142+
143+
### Changes
144+
- Updated vulnerable dependencies to secure versions
145+
- Bumped version to v$NEW_VERSION
146+
- All tests passed ✅
147+
148+
### Security Report
149+
\`\`\`
150+
$(cat security-report.txt)
151+
\`\`\`
152+
153+
### Test Results
154+
- ✅ Unit tests passed
155+
- ✅ CLI tests passed
156+
157+
### Next Steps
158+
1. Review the dependency changes
159+
2. Merge this PR to trigger release workflow
160+
3. A new release will be automatically published to PyPI
161+
162+
---
163+
🤖 This PR was automatically generated by the security workflow
164+
EOF
165+
)" \
166+
--base master \
167+
--head $BRANCH_NAME \
168+
--label "security,automated"
169+
170+
- name: Auto-merge if tests pass
171+
if: steps.run_tests.outputs.tests_passed == 'true'
172+
env:
173+
GH_TOKEN: ${{ github.token }}
174+
run: |
175+
BRANCH_NAME="security/auto-update-$(date +%Y%m%d)"
176+
177+
# Wait a moment for PR to be created
178+
sleep 5
179+
180+
# Get PR number
181+
PR_NUMBER=$(gh pr list --head $BRANCH_NAME --json number --jq '.[0].number')
182+
183+
if [ ! -z "$PR_NUMBER" ]; then
184+
# Enable auto-merge
185+
gh pr merge $PR_NUMBER --auto --squash --delete-branch
186+
187+
echo "✅ Auto-merge enabled for PR #$PR_NUMBER"
188+
echo "PR will merge automatically once all checks pass"
189+
fi
190+
191+
- name: No vulnerabilities found
192+
if: steps.pip_audit.outputs.vulnerabilities_found != 'true' && steps.safety_check.outputs.safety_issues != 'true'
193+
run: |
194+
echo "✅ No security vulnerabilities found"
195+
echo "All dependencies are up to date and secure"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = '2.0.3'
1+
__version__ = '2.1.0'
22

0 commit comments

Comments
 (0)