Skip to content

Commit 4d3736e

Browse files
chore: automate security updates and releases
- renovate.json: enable platformAutomerge + automerge for security updates immediately and minor/patch after 3-day stabilisation period - pyproject.toml: remove overly tight patch-level upper bounds on cryptography and urllib3 so Renovate security PRs can actually merge - .github/workflows/auto-release.yml: after CI passes on develop, either tag a manually-bumped version (if changelog entry exists) or auto-bump the patch version, generate a changelog entry, and push the tag — which triggers the existing release.yml to publish to PyPI Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e3fe170 commit 4d3736e

3 files changed

Lines changed: 162 additions & 12 deletions

File tree

.github/workflows/auto-release.yml

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
name: Auto Release
2+
3+
# Trigger after the Python CI workflow completes successfully on develop.
4+
# This ensures we never tag a release from a broken commit.
5+
on:
6+
workflow_run:
7+
workflows: ["Python"]
8+
types:
9+
- completed
10+
branches:
11+
- develop
12+
13+
jobs:
14+
auto-release:
15+
name: Bump version and tag release
16+
runs-on: ubuntu-latest
17+
# Only proceed when CI passed
18+
if: github.event.workflow_run.conclusion == 'success'
19+
permissions:
20+
contents: write
21+
22+
steps:
23+
- uses: actions/checkout@v4
24+
with:
25+
fetch-depth: 0
26+
# Use a PAT so the tag push can trigger release.yml
27+
# (pushes with GITHUB_TOKEN do not trigger further workflows)
28+
token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
29+
30+
- name: Read current version
31+
id: ver
32+
run: |
33+
VERSION=$(grep '^version = ' pyproject.toml | cut -d'"' -f2)
34+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
35+
echo "Current version: $VERSION"
36+
37+
- name: Check whether version is already tagged
38+
id: tag_check
39+
run: |
40+
if git tag --list | grep -qx "${{ steps.ver.outputs.version }}"; then
41+
echo "tagged=true" >> "$GITHUB_OUTPUT"
42+
else
43+
echo "tagged=false" >> "$GITHUB_OUTPUT"
44+
fi
45+
46+
# ── Path A: version already tagged → check for unreleased commits ──────
47+
48+
- name: Count commits since last tag
49+
id: commits
50+
if: steps.tag_check.outputs.tagged == 'true'
51+
run: |
52+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
53+
if [ -z "$LATEST_TAG" ]; then
54+
echo "count=0" >> "$GITHUB_OUTPUT"
55+
else
56+
COUNT=$(git log "$LATEST_TAG..HEAD" --oneline | wc -l)
57+
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
58+
echo "Commits since $LATEST_TAG: $COUNT"
59+
fi
60+
61+
- name: Bump patch version
62+
id: bump
63+
if: steps.tag_check.outputs.tagged == 'true' && steps.commits.outputs.count > 0
64+
run: |
65+
CUR="${{ steps.ver.outputs.version }}"
66+
MAJOR=$(echo "$CUR" | cut -d. -f1)
67+
MINOR=$(echo "$CUR" | cut -d. -f2)
68+
PATCH=$(echo "$CUR" | cut -d. -f3)
69+
NEW="$MAJOR.$MINOR.$((PATCH + 1))"
70+
echo "new_version=$NEW" >> "$GITHUB_OUTPUT"
71+
echo "Bumping $CUR → $NEW"
72+
sed -i "s/^version = \"$CUR\"/version = \"$NEW\"/" pyproject.toml
73+
sed -i "s/^__version__ = \"$CUR\"/__version__ = \"$NEW\"/" src/github_bot_api/__init__.py
74+
75+
- name: Create changelog entry
76+
if: steps.tag_check.outputs.tagged == 'true' && steps.commits.outputs.count > 0
77+
run: |
78+
NEW="${{ steps.bump.outputs.new_version }}"
79+
LATEST_TAG=$(git describe --tags --abbrev=0)
80+
TODAY=$(date +%Y-%m-%d)
81+
UUID=$(python3 -c "import uuid; print(uuid.uuid4())")
82+
# Collect PR/commit subjects for the description
83+
DESCRIPTION=$(git log "$LATEST_TAG..HEAD" --pretty=format:"%s" \
84+
| grep -v '^\[auto-release\]' | head -10 \
85+
| python3 -c "
86+
import sys, json
87+
lines = [l.strip() for l in sys.stdin if l.strip()]
88+
print('; '.join(lines) if lines else 'Automated dependency updates')
89+
")
90+
cat > ".changelog/$NEW.toml" << EOF
91+
release-date = "$TODAY"
92+
93+
[[entries]]
94+
id = "$UUID"
95+
type = "chore"
96+
description = "$DESCRIPTION"
97+
author = "@renovate[bot]"
98+
EOF
99+
echo "Created .changelog/$NEW.toml"
100+
101+
- name: Commit version bump and tag
102+
if: steps.tag_check.outputs.tagged == 'true' && steps.commits.outputs.count > 0
103+
run: |
104+
NEW="${{ steps.bump.outputs.new_version }}"
105+
git config user.name "github-actions[bot]"
106+
git config user.email "github-actions[bot]@users.noreply.github.com"
107+
git add pyproject.toml src/github_bot_api/__init__.py ".changelog/$NEW.toml"
108+
git commit -m "chore: bump version to $NEW [auto-release]"
109+
git push origin develop
110+
git tag "$NEW" -m "Release $NEW"
111+
git push origin "$NEW"
112+
echo "Pushed tag $NEW — release.yml will publish to PyPI"
113+
114+
# ── Path B: version bumped manually in a PR, not yet tagged ────────────
115+
116+
- name: Verify changelog exists for manual version bump
117+
if: steps.tag_check.outputs.tagged == 'false'
118+
id: changelog_check
119+
run: |
120+
VERSION="${{ steps.ver.outputs.version }}"
121+
if [ -f ".changelog/$VERSION.toml" ]; then
122+
echo "exists=true" >> "$GITHUB_OUTPUT"
123+
echo "Changelog entry found for $VERSION"
124+
else
125+
echo "exists=false" >> "$GITHUB_OUTPUT"
126+
echo "No changelog entry at .changelog/$VERSION.toml — skipping release"
127+
fi
128+
129+
- name: Tag manually-bumped version
130+
if: steps.tag_check.outputs.tagged == 'false' && steps.changelog_check.outputs.exists == 'true'
131+
run: |
132+
VERSION="${{ steps.ver.outputs.version }}"
133+
git config user.name "github-actions[bot]"
134+
git config user.email "github-actions[bot]@users.noreply.github.com"
135+
git tag "$VERSION" -m "Release $VERSION"
136+
git push origin "$VERSION"
137+
echo "Pushed tag $VERSION — release.yml will publish to PyPI"

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ authors = [
99
license = {text = "MIT"}
1010
requires-python = "<4.0,>=3.10"
1111
dependencies = [
12-
"cryptography<44.0.1,>=44.0.0",
12+
"cryptography>=44.0.0",
1313
"pygithub>=2.5.0",
14-
"PyJWT<3.0.0,>=2.6.0",
15-
"requests<3.0.0,>=2.28.2",
16-
"urllib3<2.6.4,>=2.6.3",
14+
"PyJWT>=2.6.0,<3.0.0",
15+
"requests>=2.28.2,<3.0.0",
16+
"urllib3>=2.6.3",
1717
]
1818
name = "github-bot-api"
1919
version = "0.7.1"

renovate.json

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,27 @@
11
{
22
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
3-
"extends": [
4-
"config:recommended"
5-
],
3+
"extends": ["config:recommended"],
64
"rebaseWhen": "conflicted",
5+
"labels": ["dependencies"],
6+
"platformAutomerge": true,
77
"packageRules": [
8-
{
9-
"groupName": "minor and patch updates",
10-
"matchPackagePatterns": ["*"],
11-
"matchUpdateTypes": ["minor", "patch", "pin", "digest"]
12-
}
8+
{
9+
"description": "Automerge security updates immediately",
10+
"matchPackagePatterns": ["*"],
11+
"matchCategories": ["security"],
12+
"automerge": true,
13+
"automergeType": "pr",
14+
"minimumReleaseAge": "0 days",
15+
"prPriority": 10
16+
},
17+
{
18+
"description": "Automerge minor and patch updates after 3 days stabilization",
19+
"groupName": "minor and patch updates",
20+
"matchPackagePatterns": ["*"],
21+
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
22+
"automerge": true,
23+
"automergeType": "pr",
24+
"minimumReleaseAge": "3 days"
25+
}
1326
]
1427
}

0 commit comments

Comments
 (0)