446 lines
16 KiB
YAML
446 lines
16 KiB
YAML
# 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<<EOF" >> $GITHUB_OUTPUT
|
|
cat changed_files.txt >> $GITHUB_OUTPUT
|
|
echo "" >> $GITHUB_OUTPUT
|
|
echo "EOF" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
# Store release notes
|
|
{
|
|
echo 'release_notes<<EOF'
|
|
cat changes.md
|
|
echo ""
|
|
echo 'EOF'
|
|
} >> $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<<EOF'
|
|
echo "$COMMITS"
|
|
echo ""
|
|
echo 'EOF'
|
|
} >> "$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
|
|
|