diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 0000000..772d5c8 --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -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" diff --git a/pyproject.toml b/pyproject.toml index 422142d..bcf3d8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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" diff --git a/renovate.json b/renovate.json index c12fae6..08bb46c 100644 --- a/renovate.json +++ b/renovate.json @@ -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" + } ] }