Update ABP Studio Docs #34
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Update ABP Studio Docs | |
| on: | |
| repository_dispatch: | |
| types: [update_studio_docs] | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Studio version (e.g., 2.1.10)' | |
| required: true | |
| name: | |
| description: 'Release name' | |
| required: true | |
| notes: | |
| description: 'Raw release notes' | |
| required: true | |
| url: | |
| description: 'Release URL' | |
| required: true | |
| target_branch: | |
| description: 'Target branch (leave empty to auto-detect from latest stable ABP release)' | |
| required: false | |
| default: '' | |
| jobs: | |
| update-docs: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| models: read | |
| steps: | |
| # ------------------------------------------------- | |
| # Extract payload (repository_dispatch or workflow_dispatch) | |
| # ------------------------------------------------- | |
| - name: Extract payload | |
| id: payload | |
| run: | | |
| if [ "${{ github.event_name }}" = "repository_dispatch" ]; then | |
| echo "version=${{ github.event.client_payload.version }}" >> $GITHUB_OUTPUT | |
| echo "name=${{ github.event.client_payload.name }}" >> $GITHUB_OUTPUT | |
| echo "url=${{ github.event.client_payload.url }}" >> $GITHUB_OUTPUT | |
| echo "target_branch=${{ github.event.client_payload.target_branch }}" >> $GITHUB_OUTPUT | |
| # Save notes to environment variable (multiline) | |
| { | |
| echo "RAW_NOTES<<NOTES_DELIMITER_EOF" | |
| jq -r '.client_payload.notes' "$GITHUB_EVENT_PATH" | |
| echo "NOTES_DELIMITER_EOF" | |
| } >> $GITHUB_ENV | |
| else | |
| echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT | |
| echo "name=${{ github.event.inputs.name }}" >> $GITHUB_OUTPUT | |
| echo "url=${{ github.event.inputs.url }}" >> $GITHUB_OUTPUT | |
| echo "target_branch=${{ github.event.inputs.target_branch }}" >> $GITHUB_OUTPUT | |
| # Save notes to environment variable (multiline) | |
| { | |
| echo "RAW_NOTES<<NOTES_DELIMITER_EOF" | |
| echo "${{ github.event.inputs.notes }}" | |
| echo "NOTES_DELIMITER_EOF" | |
| } >> $GITHUB_ENV | |
| fi | |
| # ------------------------------------------------- | |
| # Resolve target branch (auto-detect from latest stable ABP if not provided) | |
| # ------------------------------------------------- | |
| - name: Resolve target branch | |
| id: resolve_branch | |
| run: | | |
| TARGET_BRANCH="${{ steps.payload.outputs.target_branch }}" | |
| if [ -z "$TARGET_BRANCH" ]; then | |
| echo "🔍 No target_branch provided - fetching latest stable ABP release..." | |
| RELEASES=$(curl -fsS \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/abpframework/abp/releases?per_page=20") | |
| ABP_VERSION=$(echo "$RELEASES" | jq -r ' | |
| [.[] | select( | |
| (.prerelease == false) and | |
| (.tag_name | test("preview|rc|beta|dev"; "i") | not) | |
| )] | first | .tag_name | |
| ') | |
| if [ -z "$ABP_VERSION" ] || [ "$ABP_VERSION" = "null" ]; then | |
| echo "❌ Could not determine latest stable ABP version" | |
| exit 1 | |
| fi | |
| # Derive rel-X.Y from X.Y.Z (e.g., 10.1.1 -> rel-10.1) | |
| TARGET_BRANCH=$(echo "$ABP_VERSION" | grep -oE '^[0-9]+\.[0-9]+' | sed 's/^/rel-/') | |
| if [ -z "$TARGET_BRANCH" ]; then | |
| echo "❌ Could not derive target branch from version: $ABP_VERSION" | |
| exit 1 | |
| fi | |
| echo "✅ Auto-detected target branch: $TARGET_BRANCH (from ABP $ABP_VERSION)" | |
| else | |
| echo "✅ Using provided target branch: $TARGET_BRANCH" | |
| fi | |
| echo "target_branch=$TARGET_BRANCH" >> $GITHUB_OUTPUT | |
| - name: Validate payload | |
| env: | |
| VERSION: ${{ steps.payload.outputs.version }} | |
| NAME: ${{ steps.payload.outputs.name }} | |
| URL: ${{ steps.payload.outputs.url }} | |
| TARGET_BRANCH: ${{ steps.resolve_branch.outputs.target_branch }} | |
| run: | | |
| if [ -z "$VERSION" ] || [ "$VERSION" = "null" ]; then | |
| echo "❌ Missing: version" | |
| exit 1 | |
| fi | |
| if [ -z "$NAME" ] || [ "$NAME" = "null" ]; then | |
| echo "❌ Missing: name" | |
| exit 1 | |
| fi | |
| if [ -z "$URL" ] || [ "$URL" = "null" ]; then | |
| echo "❌ Missing: url" | |
| exit 1 | |
| fi | |
| if [ -z "$RAW_NOTES" ]; then | |
| echo "❌ Missing: release notes" | |
| exit 1 | |
| fi | |
| echo "✅ Payload validated" | |
| echo " Version: $VERSION" | |
| echo " Name: $NAME" | |
| echo " Target Branch: $TARGET_BRANCH" | |
| # ------------------------------------------------- | |
| # Checkout target branch | |
| # ------------------------------------------------- | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ steps.resolve_branch.outputs.target_branch }} | |
| fetch-depth: 0 | |
| - name: Configure git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # ------------------------------------------------- | |
| # Create working branch | |
| # ------------------------------------------------- | |
| - name: Create branch | |
| env: | |
| VERSION: ${{ steps.payload.outputs.version }} | |
| run: | | |
| BRANCH="docs/studio-${VERSION}" | |
| # Delete remote branch if exists (idempotent) | |
| git push origin --delete "$BRANCH" 2>/dev/null || true | |
| git checkout -B "$BRANCH" | |
| echo "BRANCH=$BRANCH" >> $GITHUB_ENV | |
| # ------------------------------------------------- | |
| # Restore helper scripts from the workflow commit. | |
| # The docs target branch (e.g. rel-10.4) may not contain them yet. | |
| # ------------------------------------------------- | |
| - name: Checkout workflow scripts | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: ${{ github.repository }} | |
| ref: ${{ github.sha }} | |
| sparse-checkout: .github/scripts | |
| path: _workflow_scripts | |
| - name: Install workflow scripts | |
| run: | | |
| mkdir -p .github/scripts | |
| cp _workflow_scripts/.github/scripts/*.py .github/scripts/ | |
| ls -la .github/scripts/ | |
| # ------------------------------------------------- | |
| # Analyze existing release notes format | |
| # ------------------------------------------------- | |
| - name: Analyze existing format | |
| id: analyze | |
| run: | | |
| FILE="docs/en/studio/release-notes.md" | |
| if [ -f "$FILE" ] && [ -s "$FILE" ]; then | |
| { | |
| echo "EXISTING_FORMAT<<DELIMITER_EOF" | |
| head -50 "$FILE" | sed 's/DELIMITER_EOF/DELIMITER_E0F/g' | |
| echo "DELIMITER_EOF" | |
| } >> $GITHUB_OUTPUT | |
| else | |
| { | |
| echo "EXISTING_FORMAT<<DELIMITER_EOF" | |
| echo "# ABP Studio Release Notes" | |
| echo "" | |
| echo "## 2.1.0 (2025-12-08) Latest" | |
| echo "- Enhanced Module Installation UI" | |
| echo "- Added AI Management option" | |
| echo "DELIMITER_EOF" | |
| } >> $GITHUB_OUTPUT | |
| fi | |
| # ------------------------------------------------- | |
| # Try AI formatting (OPTIONAL - never fails workflow) | |
| # ------------------------------------------------- | |
| - name: Format release notes with AI | |
| id: ai | |
| continue-on-error: true | |
| uses: actions/ai-inference@v1 | |
| with: | |
| model: openai/gpt-4.1 | |
| prompt: | | |
| You are a technical writer for ABP Studio release notes. | |
| Existing release notes format: | |
| ${{ steps.analyze.outputs.EXISTING_FORMAT }} | |
| New release: | |
| Version: ${{ steps.payload.outputs.version }} | |
| Name: ${{ steps.payload.outputs.name }} | |
| Raw notes: | |
| ${{ env.RAW_NOTES }} | |
| CRITICAL RULES: | |
| 1. Extract ONLY essential, user-facing changes | |
| 2. Format as markdown bullet points starting with "* " | |
| 3. Keep it concise, friendly and easy to scan | |
| 4. Match the style of existing release notes | |
| 5. Skip internal/technical details unless critical | |
| 6. Return ONLY the bullet points (no version header, no date) | |
| 7. One change per line | |
| 8. Prefer short action-oriented summaries like "AI Agent Upgrades: Added browser automation tools" | |
| Output example: | |
| * AI Agent Upgrades: Added browser automation tools | |
| * Module Setup Improvements: Added guidance for modularity options | |
| * UI Polish: Improved sidebar icons and visual consistency | |
| Return ONLY the formatted bullet points. | |
| # ------------------------------------------------- | |
| # Fallback: Use raw notes if AI unavailable | |
| # ------------------------------------------------- | |
| - name: Prepare final release notes | |
| run: | | |
| mkdir -p .tmp | |
| AI_RESPONSE="${{ steps.ai.outputs.response }}" | |
| if [ -n "$AI_RESPONSE" ] && [ "$AI_RESPONSE" != "null" ]; then | |
| echo "✅ Using AI-formatted release notes" | |
| echo "$AI_RESPONSE" > .tmp/final-notes.txt | |
| else | |
| echo "⚠️ AI unavailable - generating concise user-friendly summaries from raw notes" | |
| python3 .github/scripts/format-studio-release-notes.py | |
| fi | |
| # Normalize bullets to "* " even if AI returns "- ". | |
| sed -E 's/^[[:space:]]*-[[:space:]]+/* /' .tmp/final-notes.txt > .tmp/final-notes.normalized.txt | |
| mv .tmp/final-notes.normalized.txt .tmp/final-notes.txt | |
| # Safety check: verify we have content | |
| if [ ! -s .tmp/final-notes.txt ]; then | |
| echo "⚠️ No valid release notes extracted, using minimal fallback" | |
| echo "* Release ${{ steps.payload.outputs.version }}" > .tmp/final-notes.txt | |
| fi | |
| echo "=== Final release notes ===" | |
| cat .tmp/final-notes.txt | |
| echo "===========================" | |
| # ------------------------------------------------- | |
| # Update release-notes.md (move "Latest" tag correctly) | |
| # ------------------------------------------------- | |
| - name: Update release-notes.md | |
| env: | |
| VERSION: ${{ steps.payload.outputs.version }} | |
| NAME: ${{ steps.payload.outputs.name }} | |
| URL: ${{ steps.payload.outputs.url }} | |
| run: | | |
| FILE="docs/en/studio/release-notes.md" | |
| DATE="$(date +%Y-%m-%d)" | |
| mkdir -p docs/en/studio | |
| # Check if version already exists (idempotent) | |
| if [ -f "$FILE" ] && grep -q "^## $VERSION " "$FILE"; then | |
| echo "⚠️ Version $VERSION already exists in release notes - skipping update" | |
| echo "VERSION_UPDATED=false" >> $GITHUB_ENV | |
| exit 0 | |
| fi | |
| # Read final notes | |
| NOTES_CONTENT="$(cat .tmp/final-notes.txt)" | |
| # Create new entry | |
| NEW_ENTRY="## $VERSION ($DATE) Latest | |
| $NOTES_CONTENT | |
| " | |
| # Process file | |
| if [ ! -f "$FILE" ]; then | |
| # Create new file | |
| cat > "$FILE" <<EOF | |
| # ABP Studio Release Notes | |
| $NEW_ENTRY | |
| EOF | |
| else | |
| # Remove "Latest" tag from existing entries and insert new one | |
| awk -v new_entry="$NEW_ENTRY" ' | |
| BEGIN { inserted = 0 } | |
| # Remove "Latest" from existing entries | |
| /^## [0-9]/ { | |
| gsub(/ Latest$/, "", $0) | |
| } | |
| # Insert after first "## " (version heading) or after title | |
| /^## / && !inserted { | |
| print new_entry | |
| inserted = 1 | |
| } | |
| # Print current line | |
| { print } | |
| # If we reach end without inserting, add at end | |
| END { | |
| if (!inserted) { | |
| print "" | |
| print new_entry | |
| } | |
| } | |
| ' "$FILE" > "$FILE.new" | |
| mv "$FILE.new" "$FILE" | |
| fi | |
| echo "VERSION_UPDATED=true" >> $GITHUB_ENV | |
| echo "=== Updated release-notes.md preview ===" | |
| head -30 "$FILE" | |
| echo "========================================" | |
| # ------------------------------------------------- | |
| # Fetch latest stable ABP version (no preview/rc/beta) | |
| # ------------------------------------------------- | |
| - name: Fetch latest stable ABP version | |
| id: abp | |
| run: | | |
| # Fetch all releases | |
| RELEASES=$(curl -fsS \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/abpframework/abp/releases?per_page=20") | |
| # Filter stable releases (exclude preview, rc, beta, dev) | |
| ABP_VERSION=$(echo "$RELEASES" | jq -r ' | |
| [.[] | select( | |
| (.prerelease == false) and | |
| (.tag_name | test("preview|rc|beta|dev"; "i") | not) | |
| )] | first | .tag_name | |
| ') | |
| if [ -z "$ABP_VERSION" ] || [ "$ABP_VERSION" = "null" ]; then | |
| echo "❌ Could not determine latest stable ABP version" | |
| exit 1 | |
| fi | |
| echo "✅ Latest stable ABP version: $ABP_VERSION" | |
| echo "ABP_VERSION=$ABP_VERSION" >> $GITHUB_ENV | |
| # ------------------------------------------------- | |
| # Update version-mapping.md (smart range expansion) | |
| # ------------------------------------------------- | |
| - name: Update version-mapping.md | |
| env: | |
| STUDIO_VERSION: ${{ steps.payload.outputs.version }} | |
| run: | | |
| FILE="docs/en/studio/version-mapping.md" | |
| ABP_VERSION="${{ env.ABP_VERSION }}" | |
| mkdir -p docs/en/studio | |
| # Create file if doesn't exist | |
| if [ ! -f "$FILE" ]; then | |
| cat > "$FILE" <<EOF | |
| # ABP Studio and ABP Startup Template Version Mappings | |
| | **ABP Studio Version** | **ABP Version of Startup Template** | | |
| |------------------------|-------------------------------------| | |
| | $STUDIO_VERSION | $ABP_VERSION | | |
| EOF | |
| echo "MAPPING_UPDATED=true" >> $GITHUB_ENV | |
| exit 0 | |
| fi | |
| python3 .github/scripts/update-studio-version-mapping.py | |
| echo "MAPPING_UPDATED=true" >> $GITHUB_ENV | |
| echo "=== Updated version-mapping.md preview ===" | |
| head -35 "$FILE" | |
| echo "==========================================" | |
| # ------------------------------------------------- | |
| # Check for changes | |
| # ------------------------------------------------- | |
| - name: Check for changes | |
| id: changes | |
| run: | | |
| git add docs/en/studio/ | |
| if git diff --cached --quiet; then | |
| echo "has_changes=false" >> $GITHUB_OUTPUT | |
| echo "⚠️ No changes detected" | |
| else | |
| echo "has_changes=true" >> $GITHUB_OUTPUT | |
| echo "✅ Changes detected:" | |
| git diff --cached --stat | |
| fi | |
| # ------------------------------------------------- | |
| # Commit & push | |
| # ------------------------------------------------- | |
| - name: Commit and push | |
| if: steps.changes.outputs.has_changes == 'true' | |
| env: | |
| VERSION: ${{ steps.payload.outputs.version }} | |
| NAME: ${{ steps.payload.outputs.name }} | |
| run: | | |
| git commit -m "docs(studio): update documentation for release $VERSION | |
| - Updated release notes for $VERSION | |
| - Updated version mapping with ABP ${{ env.ABP_VERSION }} | |
| Release: $NAME" | |
| git push -f origin "$BRANCH" | |
| # ------------------------------------------------- | |
| # Create or update PR | |
| # ------------------------------------------------- | |
| - name: Create or update PR | |
| if: steps.changes.outputs.has_changes == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.payload.outputs.version }} | |
| NAME: ${{ steps.payload.outputs.name }} | |
| URL: ${{ steps.payload.outputs.url }} | |
| TARGET_BRANCH: ${{ steps.resolve_branch.outputs.target_branch }} | |
| run: | | |
| # Check for existing PR | |
| EXISTING_PR=$(gh pr list \ | |
| --head "$BRANCH" \ | |
| --base "$TARGET_BRANCH" \ | |
| --json number \ | |
| --jq '.[0].number' 2>/dev/null || echo "") | |
| PR_BODY="Automated documentation update for ABP Studio release **$VERSION**. | |
| ## Release Information | |
| - **Version**: $VERSION | |
| - **Name**: $NAME | |
| - **Release**: [View on GitHub]($URL) | |
| - **ABP Framework Version**: ${{ env.ABP_VERSION }} | |
| ## Changes | |
| - ✅ Updated [release-notes.md](docs/en/studio/release-notes.md) | |
| - ✅ Updated [version-mapping.md](docs/en/studio/version-mapping.md) | |
| --- | |
| *This PR was automatically generated by the [update-studio-docs workflow](.github/workflows/update-studio-docs.yml)*" | |
| if [ -n "$EXISTING_PR" ]; then | |
| echo "🔄 Updating existing PR #$EXISTING_PR" | |
| gh pr edit "$EXISTING_PR" \ | |
| --title "docs(studio): release $VERSION - $NAME" \ | |
| --body "$PR_BODY" \ | |
| --add-reviewer skoc10 | |
| echo "PR_NUMBER=$EXISTING_PR" >> $GITHUB_ENV | |
| else | |
| echo "📝 Creating new PR" | |
| sleep 2 # Wait for GitHub to sync | |
| PR_URL=$(gh pr create \ | |
| --title "docs(studio): release $VERSION - $NAME" \ | |
| --body "$PR_BODY" \ | |
| --base "$TARGET_BRANCH" \ | |
| --head "$BRANCH" \ | |
| --reviewer skoc10) | |
| PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$') | |
| echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV | |
| echo "✅ Created PR #$PR_NUMBER: $PR_URL" | |
| fi | |
| # ------------------------------------------------- | |
| # Enable auto-merge (safe with branch protection) | |
| # ------------------------------------------------- | |
| - name: Enable auto-merge | |
| if: steps.changes.outputs.has_changes == 'true' | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| continue-on-error: true | |
| run: | | |
| echo "🔄 Attempting to enable auto-merge for PR #$PR_NUMBER" | |
| gh pr merge "$PR_NUMBER" \ | |
| --auto \ | |
| --squash \ | |
| --delete-branch || { | |
| echo "⚠️ Auto-merge not available (branch protection or permissions)" | |
| echo " PR #$PR_NUMBER is ready for manual review" | |
| } | |
| # ------------------------------------------------- | |
| # Summary | |
| # ------------------------------------------------- | |
| - name: Workflow summary | |
| if: always() | |
| env: | |
| VERSION: ${{ steps.payload.outputs.version }} | |
| run: | | |
| echo "## 📚 ABP Studio Docs Update Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Version**: $VERSION" >> $GITHUB_STEP_SUMMARY | |
| echo "**Release**: ${{ steps.payload.outputs.name }}" >> $GITHUB_STEP_SUMMARY | |
| echo "**Target Branch**: ${{ steps.resolve_branch.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ steps.changes.outputs.has_changes }}" = "true" ]; then | |
| echo "### ✅ Changes Applied" >> $GITHUB_STEP_SUMMARY | |
| echo "- Release notes updated: ${{ env.VERSION_UPDATED }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- Version mapping updated: ${{ env.MAPPING_UPDATED }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- ABP Framework version: ${{ env.ABP_VERSION }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- PR: #${{ env.PR_NUMBER }}" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "### ⚠️ No Changes" >> $GITHUB_STEP_SUMMARY | |
| echo "Version $VERSION already exists in documentation." >> $GITHUB_STEP_SUMMARY | |
| fi |