name: Create Release PR on: push: branches: - main permissions: contents: write pull-requests: write jobs: release-pr: name: Create Release PR if: "!contains(gitea.event.head_commit.message, 'chore(main): release')" runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GT_TOKEN }} - name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install dependencies run: | pip install gitpython packaging - name: Create Release PR env: GIT_TOKEN: ${{ secrets.GT_TOKEN }} REPO: "TDPI/jellyfin-plugin-smartnotify" run: | python3 << 'EOF' import re import subprocess import json from packaging import version as pkg_version def get_commits_since_last_tag(): try: last_tag = subprocess.check_output(['git', 'describe', '--tags', '--abbrev=0'], text=True).strip() commits = subprocess.check_output(['git', 'log', f'{last_tag}..HEAD', '--pretty=format:%s'], text=True).strip().split('\n') except: commits = subprocess.check_output(['git', 'log', '--pretty=format:%s'], text=True).strip().split('\n') return [c for c in commits if c] def analyze_commits(commits): has_feat = any(c.startswith('feat') for c in commits) has_fix = any(c.startswith('fix') for c in commits) has_breaking = any('!' in c.split(':')[0] or 'BREAKING CHANGE' in c for c in commits) has_chore = any(c.startswith('chore') for c in commits) return has_breaking, has_feat, has_fix, has_chore def get_current_version(): try: with open('.release-please-manifest.json', 'r') as f: manifest = json.load(f) return manifest.get('.', '1.0.0') except: return '1.0.0' def bump_version(current, has_breaking, has_feat, has_fix, has_chore): v = pkg_version.parse(current) major, minor, patch = v.major, v.minor, v.micro if has_breaking: return f'{major + 1}.0.0' elif has_feat: return f'{major}.{minor + 1}.0' elif has_fix or has_chore: return f'{major}.{minor}.{patch + 1}' return None commits = get_commits_since_last_tag() if not commits or commits == ['']: print('No commits to release') exit(0) has_breaking, has_feat, has_fix, has_chore = analyze_commits(commits) if not (has_breaking or has_feat or has_fix or has_chore): print('No release-worthy commits') exit(0) current_version = get_current_version() new_version = bump_version(current_version, has_breaking, has_feat, has_fix, has_chore) if not new_version: print('No version bump needed') exit(0) print(f'Bumping version from {current_version} to {new_version}') # Update version in manifest with open('.release-please-manifest.json', 'r') as f: manifest = json.load(f) manifest['.'] = new_version with open('.release-please-manifest.json', 'w') as f: json.dump(manifest, f, indent=2) # Update version in .csproj csproj_path = 'Jellyfin.Plugin.SmartNotify/Jellyfin.Plugin.SmartNotify.csproj' with open(csproj_path, 'r') as f: csproj = f.read() csproj = re.sub(r'.*?', f'{new_version}.0', csproj) csproj = re.sub(r'.*?', f'{new_version}.0', csproj) with open(csproj_path, 'w') as f: f.write(csproj) # Generate CHANGELOG changelog_entry = f'## {new_version} ({subprocess.check_output(["date", "+%Y-%m-%d"], text=True).strip()})\n\n' if has_breaking: changelog_entry += '### BREAKING CHANGES\n\n' for c in commits: if '!' in c.split(':')[0] or 'BREAKING CHANGE' in c: changelog_entry += f'* {c}\n' changelog_entry += '\n' if has_feat: changelog_entry += '### Features\n\n' for c in commits: if c.startswith('feat'): changelog_entry += f'* {c}\n' changelog_entry += '\n' if has_fix: changelog_entry += '### Bug Fixes\n\n' for c in commits: if c.startswith('fix'): changelog_entry += f'* {c}\n' changelog_entry += '\n' if has_chore: changelog_entry += '### Chores\n\n' for c in commits: if c.startswith('chore'): changelog_entry += f'* {c}\n' changelog_entry += '\n' try: with open('CHANGELOG.md', 'r') as f: old_changelog = f.read() except: old_changelog = '# Changelog\n\n' with open('CHANGELOG.md', 'w') as f: f.write('# Changelog\n\n' + changelog_entry + '\n' + old_changelog.replace('# Changelog\n', '').lstrip()) subprocess.run(['git', 'config', 'user.name', 'Gitea Actions']) subprocess.run(['git', 'config', 'user.email', 'actions@git.tdpi.dev']) subprocess.run(['git', 'add', '.release-please-manifest.json', 'CHANGELOG.md', csproj_path]) subprocess.run(['git', 'commit', '-m', f'chore(main): release {new_version}']) with open('/tmp/new_version', 'w') as f: f.write(new_version) EOF NEW_VERSION=$(cat /tmp/new_version 2>/dev/null || echo "") if [ -z "$NEW_VERSION" ]; then echo "No version bump needed" exit 0 fi BRANCH_NAME="release-please--branches--main" # Push changes if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then git push -f origin HEAD:"$BRANCH_NAME" PR_EXISTS=true else git push origin HEAD:"$BRANCH_NAME" PR_EXISTS=false fi # Get changelog content for PR body CHANGELOG_CONTENT=$(awk '/^## '"$NEW_VERSION"'/{flag=1} /^## / && flag && !/^## '"$NEW_VERSION"'/{exit} flag' CHANGELOG.md | jq -Rs .) if [ "$PR_EXISTS" = true ]; then PR_NUMBER=$(curl -s "https://git.tdpi.dev/api/v1/repos/$REPO/pulls?state=open&head=$BRANCH_NAME" \ -H "Authorization: token $GIT_TOKEN" | jq -r '.[0].number') if [ "$PR_NUMBER" != "null" ]; then curl -X PATCH "https://git.tdpi.dev/api/v1/repos/$REPO/pulls/$PR_NUMBER" \ -H "Authorization: token $GIT_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"title\": \"chore(main): release $NEW_VERSION\", \"body\": $CHANGELOG_CONTENT }" fi else curl -X POST "https://git.tdpi.dev/api/v1/repos/$REPO/pulls" \ -H "Authorization: token $GIT_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"title\": \"chore(main): release $NEW_VERSION\", \"head\": \"$BRANCH_NAME\", \"base\": \"main\", \"body\": $CHANGELOG_CONTENT }" fi