# Plugin Release Workflow # # This workflow automates the release process for OpenWebUI plugins. # # Triggers: # - Push to main branch when plugins are modified (auto-release) # - Manual trigger (workflow_dispatch) with custom release notes # - Push of version tags (v*) # # What it does: # 1. Detects plugin version changes compared to the last release # 2. Generates release notes with updated plugin information # 3. Creates a GitHub Release with plugin files as downloadable assets # 4. Supports multiple plugin updates in a single release name: Plugin Release on: # Auto-trigger on push to main when plugins are modified push: branches: - main paths: - 'plugins/**/*.py' tags: - 'v*' # Manual trigger with inputs workflow_dispatch: inputs: version: description: 'Release version (e.g., v1.0.0). Leave empty for auto-generated version.' required: false type: string release_title: description: 'Release title (optional)' required: false type: string release_notes: description: 'Additional release notes (Markdown)' required: false type: string prerelease: description: 'Mark as pre-release' required: false type: boolean default: false permissions: contents: write jobs: check-changes: runs-on: ubuntu-latest env: LANG: en_US.UTF-8 LC_ALL: en_US.UTF-8 outputs: has_changes: ${{ steps.detect.outputs.has_changes }} changed_plugins: ${{ steps.detect.outputs.changed_plugins }} release_notes: ${{ steps.detect.outputs.release_notes }} steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Configure Git run: | git config --global core.quotepath false git config --global i18n.commitencoding utf-8 git config --global i18n.logoutputencoding utf-8 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Detect plugin changes id: detect run: | # Get the last release tag LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") if [ -z "$LAST_TAG" ]; then echo "No previous release found, treating all plugins as new" COMPARE_REF="$(git rev-list --max-parents=0 HEAD)" else echo "Comparing with last release: $LAST_TAG" COMPARE_REF="$LAST_TAG" fi # Get current plugin versions python scripts/extract_plugin_versions.py --json --output current_versions.json # Get previous plugin versions by checking out old plugins if git worktree add /tmp/old_repo ${COMPARE_REF} 2>/dev/null; then if [ -d /tmp/old_repo/plugins ]; then python scripts/extract_plugin_versions.py --plugins-dir /tmp/old_repo/plugins --json --output old_versions.json else echo "[]" > old_versions.json fi git worktree remove /tmp/old_repo 2>/dev/null || true else echo "Failed to create worktree, using empty version list" echo "[]" > old_versions.json fi # Compare versions and generate release notes python scripts/extract_plugin_versions.py --compare old_versions.json --ignore-removed --output changes.md python scripts/extract_plugin_versions.py --compare old_versions.json --json --output changes.json echo "=== Version Changes ===" cat changes.md # Check if there are any changes if grep -q "No changes detected" changes.md; then echo "has_changes=false" >> $GITHUB_OUTPUT echo "changed_plugins=" >> $GITHUB_OUTPUT else echo "has_changes=true" >> $GITHUB_OUTPUT # Extract changed plugin file paths using Python python3 -c " import json with open('changes.json', 'r') as f: data = json.load(f) files = [] for plugin in data.get('added', []): if 'file_path' in plugin: files.append(plugin['file_path']) for update in data.get('updated', []): if 'current' in update and 'file_path' in update['current']: files.append(update['current']['file_path']) print('\n'.join(files)) " > changed_files.txt echo "changed_plugins<> $GITHUB_OUTPUT cat changed_files.txt >> $GITHUB_OUTPUT echo "" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT fi # Store release notes { echo 'release_notes<> $GITHUB_OUTPUT release: needs: check-changes if: needs.check-changes.outputs.has_changes == 'true' || github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest env: LANG: en_US.UTF-8 LC_ALL: en_US.UTF-8 steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Configure Git run: | git config --global core.quotepath false git config --global i18n.commitencoding utf-8 git config --global i18n.logoutputencoding utf-8 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Determine version id: version env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ] && [ -n "${{ github.event.inputs.version }}" ]; then VERSION="${{ github.event.inputs.version }}" elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then VERSION="${GITHUB_REF#refs/tags/}" else # Auto-generate version based on date and daily release count TODAY=$(date +'%Y.%m.%d') TODAY_PREFIX="v${TODAY}-" # Count existing releases with today's date prefix # grep -c returns 1 if count is 0, so we use || true to avoid script failure EXISTING_COUNT=$(gh release list --limit 100 2>/dev/null | grep -c "^${TODAY_PREFIX}" || true) # Clean up output (handle potential newlines or fallback issues) EXISTING_COUNT=$(echo "$EXISTING_COUNT" | tr -cd '0-9') if [ -z "$EXISTING_COUNT" ]; then EXISTING_COUNT=0; fi NEXT_NUM=$((EXISTING_COUNT + 1)) VERSION="${TODAY_PREFIX}${NEXT_NUM}" # Final fallback to ensure VERSION is never empty if [ -z "$VERSION" ]; then VERSION="v$(date +'%Y.%m.%d-%H%M%S')" fi fi echo "version=$VERSION" >> $GITHUB_OUTPUT echo "Release version: $VERSION" - name: Extract plugin versions id: plugins run: | python scripts/extract_plugin_versions.py --json --output plugin_versions.json python scripts/extract_plugin_versions.py --json --output plugin_versions.json - name: Collect plugin files for release id: collect_files run: | mkdir -p release_plugins CHANGED_PLUGINS="${{ needs.check-changes.outputs.changed_plugins }}" if [ -n "$CHANGED_PLUGINS" ]; then echo "Collecting changed plugin files..." echo "$CHANGED_PLUGINS" | while read -r file; do if [ -n "$file" ] && [ -f "$file" ]; then dir=$(dirname "$file") mkdir -p "release_plugins/$dir" cp "$file" "release_plugins/$file" echo "Added: $file" fi done else echo "No changed plugins detected. Skipping file collection." fi # Create a zip file with error handling # cd release_plugins # Zip step removed as per user request echo "=== Collected Files ===" find release_plugins -name "*.py" -type f | head -20 - name: Update plugin icon URLs run: | echo "Updating icon_url in plugins to use absolute GitHub URLs..." # Base URL for raw content using the release tag REPO_URL="https://raw.githubusercontent.com/${{ github.repository }}/${{ steps.version.outputs.version }}" find release_plugins -name "*.py" | while read -r file; do # $file is like release_plugins/plugins/actions/infographic/infographic.py # Remove release_plugins/ prefix to get the path in the repo src_file="${file#release_plugins/}" src_dir=$(dirname "$src_file") base_name=$(basename "$src_file" .py) # Check if a corresponding png exists in the source repository png_file="${src_dir}/${base_name}.png" if [ -f "$png_file" ]; then echo "Found icon for $src_file: $png_file" TARGET_ICON_URL="${REPO_URL}/${png_file}" # Use python for safe replacement python3 -c " import sys import re file_path = '$file' icon_url = '$TARGET_ICON_URL' try: with open(file_path, 'r', encoding='utf-8') as f: content = f.read() # Replace icon_url: ... with new url # Matches 'icon_url: ...' and replaces it new_content = re.sub(r'^icon_url:.*$', f'icon_url: {icon_url}', content, flags=re.MULTILINE) with open(file_path, 'w', encoding='utf-8') as f: f.write(new_content) print(f'Successfully updated icon_url in {file_path}') except Exception as e: print(f'Error updating {file_path}: {e}', file=sys.stderr) sys.exit(1) " fi done - name: Debug Filenames run: | python3 -c "import sys; print(f'Filesystem encoding: {sys.getfilesystemencoding()}')" ls -R release_plugins - name: Upload Debug Artifacts uses: actions/upload-artifact@v4 with: name: debug-plugins path: release_plugins/ - name: Get commit messages id: commits if: github.event_name == 'push' run: | LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") if [ -n "$LAST_TAG" ]; then COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s" --no-merges -- plugins/ | head -20) else COMMITS=$(git log --pretty=format:"- %s" --no-merges -10 -- plugins/) fi { echo 'commits<> "$GITHUB_OUTPUT" - name: Generate release notes id: notes env: VERSION: ${{ steps.version.outputs.version }} TITLE: ${{ github.event.inputs.release_title }} NOTES: ${{ github.event.inputs.release_notes }} DETECTED_CHANGES: ${{ needs.check-changes.outputs.release_notes }} COMMITS: ${{ steps.commits.outputs.commits }} run: | > release_notes.md if [ -n "$TITLE" ]; then echo "## $TITLE" >> release_notes.md echo "" >> release_notes.md fi if [ -n "$DETECTED_CHANGES" ] && ! echo "$DETECTED_CHANGES" | grep -q "No changes detected"; then echo "## What's Changed" >> release_notes.md echo "" >> release_notes.md echo "$DETECTED_CHANGES" >> release_notes.md echo "" >> release_notes.md fi if [ -n "$COMMITS" ]; then echo "## Commits" >> release_notes.md echo "" >> release_notes.md echo "$COMMITS" >> release_notes.md echo "" >> release_notes.md fi if [ -n "$NOTES" ]; then echo "## Additional Notes" >> release_notes.md echo "" >> release_notes.md echo "$NOTES" >> release_notes.md echo "" >> release_notes.md fi cat >> release_notes.md << 'EOF' ## Download 📦 **Download the updated plugin files below** ### Installation #### From OpenWebUI Community 1. Open OpenWebUI Admin Panel 2. Navigate to Functions/Tools 3. Search for the plugin name 4. Click Install #### Manual Installation 1. Download the plugin file (`.py`) from the assets below 2. Open OpenWebUI Admin Panel → Functions 3. Click "Create Function" → Import 4. Paste the plugin code --- 📚 [Documentation](https://fu-jie.github.io/openwebui-extensions/) 🐛 [Report Issues](https://github.com/Fu-Jie/openwebui-extensions/issues) EOF echo "=== Release Notes ===" cat release_notes.md - name: Create Git Tag run: | VERSION="${{ steps.version.outputs.version }}" if [ -z "$VERSION" ]; then echo "Error: Version is empty!" exit 1 fi if ! git rev-parse "$VERSION" >/dev/null 2>&1; then echo "Creating tag $VERSION" git tag "$VERSION" git push origin "$VERSION" else echo "Tag $VERSION already exists" fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.version.outputs.version }} target_commitish: ${{ github.sha }} name: ${{ github.event.inputs.release_title || steps.version.outputs.version }} body_path: release_notes.md prerelease: ${{ github.event.inputs.prerelease || false }} make_latest: true files: | plugin_versions.json env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Release Assets env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | # Check if there are any .py files to upload if [ -d release_plugins ] && [ -n "$(find release_plugins -type f -name '*.py' 2>/dev/null)" ]; then echo "Uploading plugin files..." find release_plugins -type f -name "*.py" -print0 | xargs -0 gh release upload ${{ steps.version.outputs.version }} --clobber else echo "No plugin files to upload. Skipping asset upload." fi - name: Summary run: | echo "## 🚀 Release Created Successfully!" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Version:** ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "### Updated Plugins" >> $GITHUB_STEP_SUMMARY echo "${{ needs.check-changes.outputs.release_notes }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY