Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions .github/workflows/auto-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
name: Auto Release

# Trigger after the Python CI workflow completes successfully on develop.
# This ensures we never tag a release from a broken commit.
on:
workflow_run:
workflows: ["Python"]
types:
- completed
branches:
- develop

jobs:
auto-release:
name: Bump version and tag release
runs-on: ubuntu-latest
# Only proceed when CI passed
if: github.event.workflow_run.conclusion == 'success'
permissions:
contents: write

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Use a PAT so the tag push can trigger release.yml
# (pushes with GITHUB_TOKEN do not trigger further workflows)
token: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}

- name: Read current version
id: ver
run: |
VERSION=$(grep '^version = ' pyproject.toml | cut -d'"' -f2)
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Current version: $VERSION"

- name: Check whether version is already tagged
id: tag_check
run: |
if git tag --list | grep -qx "${{ steps.ver.outputs.version }}"; then
echo "tagged=true" >> "$GITHUB_OUTPUT"
else
echo "tagged=false" >> "$GITHUB_OUTPUT"
fi

# ── Path A: version already tagged → check for unreleased commits ──────

- name: Count commits since last tag
id: commits
if: steps.tag_check.outputs.tagged == 'true'
run: |
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$LATEST_TAG" ]; then
echo "count=0" >> "$GITHUB_OUTPUT"
else
COUNT=$(git log "$LATEST_TAG..HEAD" --oneline | wc -l)
echo "count=$COUNT" >> "$GITHUB_OUTPUT"
echo "Commits since $LATEST_TAG: $COUNT"
fi

- name: Bump patch version
id: bump
if: steps.tag_check.outputs.tagged == 'true' && steps.commits.outputs.count > 0
run: |
CUR="${{ steps.ver.outputs.version }}"
MAJOR=$(echo "$CUR" | cut -d. -f1)
MINOR=$(echo "$CUR" | cut -d. -f2)
PATCH=$(echo "$CUR" | cut -d. -f3)
NEW="$MAJOR.$MINOR.$((PATCH + 1))"
echo "new_version=$NEW" >> "$GITHUB_OUTPUT"
echo "Bumping $CUR → $NEW"
sed -i "s/^version = \"$CUR\"/version = \"$NEW\"/" pyproject.toml
sed -i "s/^__version__ = \"$CUR\"/__version__ = \"$NEW\"/" src/github_bot_api/__init__.py

- name: Create changelog entry
if: steps.tag_check.outputs.tagged == 'true' && steps.commits.outputs.count > 0
run: |
NEW="${{ steps.bump.outputs.new_version }}"
LATEST_TAG=$(git describe --tags --abbrev=0)
TODAY=$(date +%Y-%m-%d)
UUID=$(python3 -c "import uuid; print(uuid.uuid4())")
# Collect PR/commit subjects for the description
DESCRIPTION=$(git log "$LATEST_TAG..HEAD" --pretty=format:"%s" \
| grep -v '^\[auto-release\]' | head -10 \
| python3 -c "
import sys, json
lines = [l.strip() for l in sys.stdin if l.strip()]
print('; '.join(lines) if lines else 'Automated dependency updates')
")
cat > ".changelog/$NEW.toml" << EOF
release-date = "$TODAY"

[[entries]]
id = "$UUID"
type = "chore"
description = "$DESCRIPTION"
author = "@renovate[bot]"
EOF
echo "Created .changelog/$NEW.toml"

- name: Commit version bump and tag
if: steps.tag_check.outputs.tagged == 'true' && steps.commits.outputs.count > 0
run: |
NEW="${{ steps.bump.outputs.new_version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add pyproject.toml src/github_bot_api/__init__.py ".changelog/$NEW.toml"
git commit -m "chore: bump version to $NEW [auto-release]"
git push origin develop
git tag "$NEW" -m "Release $NEW"
git push origin "$NEW"
echo "Pushed tag $NEW — release.yml will publish to PyPI"

# ── Path B: version bumped manually in a PR, not yet tagged ────────────

- name: Verify changelog exists for manual version bump
if: steps.tag_check.outputs.tagged == 'false'
id: changelog_check
run: |
VERSION="${{ steps.ver.outputs.version }}"
if [ -f ".changelog/$VERSION.toml" ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Changelog entry found for $VERSION"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
echo "No changelog entry at .changelog/$VERSION.toml — skipping release"
fi

- name: Tag manually-bumped version
if: steps.tag_check.outputs.tagged == 'false' && steps.changelog_check.outputs.exists == 'true'
run: |
VERSION="${{ steps.ver.outputs.version }}"
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag "$VERSION" -m "Release $VERSION"
git push origin "$VERSION"
echo "Pushed tag $VERSION — release.yml will publish to PyPI"
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ authors = [
license = {text = "MIT"}
requires-python = "<4.0,>=3.10"
dependencies = [
"cryptography<44.0.1,>=44.0.0",
"cryptography>=44.0.0",
"pygithub>=2.5.0",
"PyJWT<3.0.0,>=2.6.0",
"requests<3.0.0,>=2.28.2",
"urllib3<2.6.4,>=2.6.3",
"PyJWT>=2.6.0,<3.0.0",
"requests>=2.28.2,<3.0.0",
"urllib3>=2.6.3",
]
name = "github-bot-api"
version = "0.7.1"
Expand Down
29 changes: 21 additions & 8 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"extends": ["config:recommended"],
"rebaseWhen": "conflicted",
"labels": ["dependencies"],
"platformAutomerge": true,
"packageRules": [
{
"groupName": "minor and patch updates",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["minor", "patch", "pin", "digest"]
}
{
"description": "Automerge security updates immediately",
"matchPackagePatterns": ["*"],
"matchCategories": ["security"],
"automerge": true,
"automergeType": "pr",
"minimumReleaseAge": "0 days",
"prPriority": 10
},
{
"description": "Automerge minor and patch updates after 3 days stabilization",
"groupName": "minor and patch updates",
"matchPackagePatterns": ["*"],
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true,
"automergeType": "pr",
"minimumReleaseAge": "3 days"
}
]
}
Loading