Files
Fu-Jie_openwebui-extensions/.github/workflows/release.yml
fujie 3e8b15af46 ci(release): include documentation content in release body
- embed changed v*.md release note content into generated GitHub release notes

- keep additional doc changes visible as file list in release output

- refine v0.9.1 EN/CN release notes to include only actual updated/fixed items
2026-03-04 00:57:18 +08:00

513 lines
18 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'
- 'plugins/**/README.md'
- 'plugins/**/README_CN.md'
- 'plugins/**/v*.md'
- 'plugins/**/v*_CN.md'
- 'docs/plugins/**/*.md'
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 }}
has_doc_changes: ${{ steps.detect.outputs.has_doc_changes }}
changed_doc_files: ${{ steps.detect.outputs.changed_doc_files }}
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
OLD_WORKTREE=$(mktemp -d)
if git worktree add "$OLD_WORKTREE" ${COMPARE_REF} 2>/dev/null; then
if [ -d "$OLD_WORKTREE/plugins" ]; then
python scripts/extract_plugin_versions.py --plugins-dir "$OLD_WORKTREE/plugins" --json --output old_versions.json
else
echo "[]" > old_versions.json
fi
git worktree remove "$OLD_WORKTREE" 2>/dev/null || true
else
echo "Failed to create worktree, using empty version list"
echo "[]" > old_versions.json
fi
rm -rf "$OLD_WORKTREE" 2>/dev/null || true
# 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
# Detect documentation/release-note changes that should be reflected in release notes
git diff --name-only "$COMPARE_REF"..HEAD -- \
'plugins/**/README.md' \
'plugins/**/README_CN.md' \
'plugins/**/v*.md' \
'plugins/**/v*_CN.md' \
'docs/plugins/**/*.md' > changed_docs.txt || true
if [ -s changed_docs.txt ]; then
echo "has_doc_changes=true" >> $GITHUB_OUTPUT
echo "changed_doc_files<<EOF" >> $GITHUB_OUTPUT
cat changed_docs.txt >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "has_doc_changes=false" >> $GITHUB_OUTPUT
echo "changed_doc_files=" >> $GITHUB_OUTPUT
fi
# Check if there are any changes
if grep -q "No changes detected" changes.md; then
if [ -s changed_docs.txt ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
fi
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
if [ -s changed_docs.txt ]; then
echo ""
echo "## Documentation Changes"
echo ""
sed 's/^/- /' changed_docs.txt
fi
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
- 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 }}
DOC_FILES: ${{ needs.check-changes.outputs.changed_doc_files }}
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
if [ -n "$DOC_FILES" ]; then
echo "## Documentation Content" >> release_notes.md
echo "" >> release_notes.md
# Prefer release-note files (v*.md / v*_CN.md), then include other doc files
RELEASE_NOTE_FILES=$(echo "$DOC_FILES" | grep -E '^plugins/.*/v[^/]*\.md$' || true)
OTHER_DOC_FILES=$(echo "$DOC_FILES" | grep -Ev '^plugins/.*/v[^/]*\.md$' || true)
if [ -n "$RELEASE_NOTE_FILES" ]; then
while IFS= read -r file; do
[ -z "$file" ] && continue
if [ -f "$file" ]; then
echo "### ${file}" >> release_notes.md
echo "" >> release_notes.md
cat "$file" >> release_notes.md
echo "" >> release_notes.md
fi
done <<< "$RELEASE_NOTE_FILES"
fi
if [ -n "$OTHER_DOC_FILES" ]; then
echo "### Additional Documentation Files" >> release_notes.md
echo "" >> release_notes.md
echo "$OTHER_DOC_FILES" | sed 's/^/- /' >> release_notes.md
echo "" >> release_notes.md
fi
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
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
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