# Plugin Update Workflow **Last Updated:** January 9, 2026 **Status:** Production **Scope:** How to release plugin updates and what happens automatically vs manually --- ## Table of Contents 1. [Update Workflow Overview](#1-update-workflow-overview) 2. [What Happens Automatically](#2-what-happens-automatically) 3. [What Requires Manual Action](#3-what-requires-manual-action) 4. [Step-by-Step: Releasing a New Version](#4-step-by-step-releasing-a-new-version) 5. [Post-Update Verification Checklist](#5-post-update-verification-checklist) 6. [Version Numbering](#6-version-numbering) 7. [Rollback Procedure](#7-rollback-procedure) 8. [Emergency Updates](#8-emergency-updates) --- ## 1. Update Workflow Overview ### The Big Picture ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Plugin Update Lifecycle │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ │ │ │ 1. DEVELOP │ Make changes to plugin source code │ │ │ ↓ │ Location: /plugins/wordpress/source/igny8-wp-bridge/ │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ │ │ │ 2. VERSION │ Update version in PHP header + constant │ │ │ ↓ │ File: igny8-bridge.php │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ │ │ │ 3. RELEASE │ Create PluginVersion in Django admin │ │ │ ↓ │ Set status = "released" or "update_ready" │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ │ │ │ 4. AUTO-BUILD│ ✅ AUTOMATIC: ZIP created, checksums calculated │ │ │ ↓ │ Signal handler builds package on status change │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ │ │ │ 5. VERIFY │ Test download, check contents, verify endpoints │ │ │ ↓ │ See verification checklist below │ │ └──────────────┘ │ │ │ │ ┌──────────────┐ │ │ │ 6. AVAILABLE │ WordPress sites can now see and install update │ │ │ │ Users update via WP admin or auto-update │ │ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` --- ## 2. What Happens Automatically When you change a `PluginVersion` status to `released` or `update_ready`, the following happens **automatically** via Django signals: ### ✅ Automatic Actions | Action | Details | |--------|---------| | **ZIP Creation** | Source files packaged into distribution ZIP | | **Version Update** | Version number updated in PHP files inside ZIP | | **File Cleanup** | Tests, .git, __pycache__, .bak files removed from ZIP | | **Checksum Calculation** | SHA256 hash generated | | **File Size Recording** | Byte count stored in database | | **File Path Update** | `file_path` field populated | | **Released Date** | `released_at` timestamp set | | **Symlink Update** | `*-latest.zip` symlink updated | ### How It Works (Signal Handler) ```python # backend/igny8_core/plugins/signals.py @receiver(pre_save, sender=PluginVersion) def auto_build_plugin_on_release(sender, instance, **kwargs): """ Triggered when PluginVersion.status changes to: - 'released' - 'update_ready' Actions: 1. Calls create_plugin_zip() utility 2. Updates instance.file_path 3. Updates instance.file_size 4. Updates instance.checksum 5. Sets instance.released_at """ ``` ### What WordPress Sites See Once released, the `check-update` API returns: ```json { "update_available": true, "latest_version": "1.2.0", "download_url": "https://api.igny8.com/api/plugins/igny8-wp-bridge/download/", "changelog": "Your changelog here" } ``` WordPress will show "Update Available" in: - Plugins page - Dashboard → Updates - Admin bar (if enabled) --- ## 3. What Requires Manual Action ### ❌ Manual Steps Required | Action | When | How | |--------|------|-----| | **Update Source Version** | Before release | Edit `igny8-bridge.php` header | | **Create PluginVersion Record** | Each release | Django admin or management command | | **Write Changelog** | Each release | Enter in PluginVersion.changelog | | **Verify After Release** | After status change | Run verification checklist | | **Mark Old Version Deprecated** | After successful release | Change old version status | ### Source Version Update Locations When releasing a new version, update these in the source: **File:** `/plugins/wordpress/source/igny8-wp-bridge/igny8-bridge.php` ```php /** * Plugin Name: IGNY8 WordPress Bridge * Version: 1.2.0 ← UPDATE THIS */ define('IGNY8_BRIDGE_VERSION', '1.2.0'); ← AND THIS ``` **Note:** The ZIP build process also updates these, but it's good practice to keep source in sync. --- ## 4. Step-by-Step: Releasing a New Version ### Step 1: Make Code Changes ```bash cd /data/app/igny8/plugins/wordpress/source/igny8-wp-bridge/ # Make your changes to PHP files # Test locally if possible ``` ### Step 2: Update Version Number Edit `igny8-bridge.php`: ```php /** * Plugin Name: IGNY8 WordPress Bridge * Version: 1.2.0 ← New version */ define('IGNY8_BRIDGE_VERSION', '1.2.0'); ← Match here ``` ### Step 3: Create PluginVersion in Django Admin 1. Go to: `https://api.igny8.com/backend/` 2. Navigate to: **Plugin Distribution** → **Plugin Versions** 3. Click **Add Plugin Version** 4. Fill in: - **Plugin:** IGNY8 WordPress Bridge - **Version:** 1.2.0 - **Status:** draft (initially) - **Changelog:** Describe changes - **Min PHP Version:** 7.4 (or higher if needed) 5. Click **Save** ### Step 4: Release the Version 1. Edit the PluginVersion you just created 2. Change **Status** to `released` 3. Click **Save** **What happens:** Signal triggers auto-build → ZIP created → database updated ### Step 5: Verify Release Run the [verification checklist](#5-post-update-verification-checklist) below. ### Step 6: Deprecate Old Version (Optional) 1. Find the previous version in Django admin 2. Change **Status** to `deprecated` 3. Save --- ## 5. Post-Update Verification Checklist ### Immediate Checks (Do Every Release) ```bash # 1. Verify ZIP exists ls -lh /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.2.0.zip # 2. Verify symlink updated ls -la /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-latest.zip # 3. Test download endpoint curl -s -o /dev/null -w "%{http_code} - %{size_download} bytes\n" \ https://api.igny8.com/api/plugins/igny8-wp-bridge/download/ # 4. Test check-update endpoint curl -s "https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0" | python3 -m json.tool # 5. Test info endpoint curl -s https://api.igny8.com/api/plugins/igny8-wp-bridge/info/ | python3 -m json.tool # 6. Verify version in ZIP unzip -p /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.2.0.zip \ igny8-wp-bridge/igny8-bridge.php | grep -E "Version:|IGNY8_BRIDGE_VERSION" # 7. Verify API URL in ZIP (must be api.igny8.com) unzip -p /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v1.2.0.zip \ igny8-wp-bridge/igny8-bridge.php | grep "api.igny8.com" ``` ### Database Verification ```bash # Check database has correct values docker exec igny8_backend python manage.py shell -c " from igny8_core.plugins.models import Plugin, PluginVersion p = Plugin.objects.get(slug='igny8-wp-bridge') v = p.get_latest_version() print(f'Version: {v.version}') print(f'Status: {v.status}') print(f'File path: {v.file_path}') print(f'File size: {v.file_size}') print(f'Checksum: {v.checksum[:32]}...') print(f'Released at: {v.released_at}') " ``` ### Expected Results | Check | Expected | |-------|----------| | Download returns | 200 status, ~150-200KB | | check-update shows | `"latest_version": "1.2.0"` | | info shows | `"version": "1.2.0"` | | ZIP version header | `Version: 1.2.0` | | API URL | `api.igny8.com` (NOT app.igny8.com) | ### WordPress Verification (If Possible) 1. Go to a test WordPress site with plugin installed 2. Navigate to **Dashboard** → **Updates** → **Check Again** 3. Verify "IGNY8 WordPress Bridge" shows update available 4. Click "View version details" → verify changelog appears 5. Click "Update Now" → verify update completes successfully --- ## 6. Version Numbering ### Semantic Versioning Format: `MAJOR.MINOR.PATCH` (e.g., 1.2.3) | Part | When to Increment | |------|-------------------| | **MAJOR** | Breaking changes, incompatible API changes | | **MINOR** | New features, backwards compatible | | **PATCH** | Bug fixes, minor improvements | ### Version Code Calculation Used for numeric comparison in database: ``` 1.0.0 → 10000 1.0.1 → 10001 1.2.0 → 10200 1.2.3 → 10203 2.0.0 → 20000 ``` Formula: `(MAJOR * 10000) + (MINOR * 100) + PATCH` --- ## 7. Rollback Procedure ### If Update Causes Issues **Option 1: Quick Rollback via Database** ```bash # Make old version the "latest" by changing status docker exec igny8_backend python manage.py shell -c " from igny8_core.plugins.models import PluginVersion # Demote new version new = PluginVersion.objects.get(plugin__slug='igny8-wp-bridge', version='1.2.0') new.status = 'deprecated' new.save() # Promote old version back old = PluginVersion.objects.get(plugin__slug='igny8-wp-bridge', version='1.1.1') old.status = 'released' old.save() print('Rollback complete') " ``` **Option 2: Keep Old ZIP Files** Old ZIP files are preserved in `dist/`. To serve an old version: ```bash # Update symlink to point to old version cd /data/app/igny8/plugins/wordpress/dist/ ln -sf igny8-wp-bridge-v1.1.1.zip igny8-wp-bridge-latest.zip ``` ### Retention Policy Keep at least 3 previous version ZIPs for emergency rollback: - Current release - Previous release - One before that --- ## 8. Emergency Updates ### For Critical Security Fixes 1. **Set `force_update = True`** on the new PluginVersion 2. This flag signals WordPress sites that update is mandatory 3. Sites with auto-updates enabled will update immediately ### Emergency Release Checklist ```bash # 1. Make the fix in source cd /data/app/igny8/plugins/wordpress/source/igny8-wp-bridge/ # ... make changes ... # 2. Update version (use PATCH increment) # Edit igny8-bridge.php # 3. Create version in Django admin with: # - Status: update_ready # - Force Update: ✓ (checked) # - Changelog: "SECURITY: [description]" # 4. Verify immediately curl https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0 # Response should include: # "force_update": true ``` --- ## Quick Reference Card ### Release New Version ``` 1. Edit source code 2. Update version in igny8-bridge.php (header + constant) 3. Create PluginVersion in Django admin (status: draft) 4. Change status to "released" → Auto-build triggers 5. Run verification checklist 6. Deprecate old version ``` ### Verification Commands ```bash # Test all endpoints curl -I https://api.igny8.com/api/plugins/igny8-wp-bridge/download/ curl https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0 curl https://api.igny8.com/api/plugins/igny8-wp-bridge/info/ # Check ZIP contents unzip -l /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-v*.zip | head -20 ``` ### Emergency Rollback ```bash # Swap versions in database docker exec igny8_backend python manage.py shell -c " from igny8_core.plugins.models import PluginVersion PluginVersion.objects.filter(version='NEW').update(status='deprecated') PluginVersion.objects.filter(version='OLD').update(status='released') " ``` --- ## Related Documentation - [WORDPRESS-INTEGRATION.md](WORDPRESS-INTEGRATION.md) - Full integration guide - [docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md](/docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md) - Original system design