14 KiB
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
- Update Workflow Overview
- What Happens Automatically
- What Requires Manual Action
- Step-by-Step: Releasing a New Version
- Post-Update Verification Checklist
- Version Numbering
- Rollback Procedure
- 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)
# 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:
{
"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
/**
* 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
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:
/**
* 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
- Go to:
https://api.igny8.com/backend/ - Navigate to: Plugin Distribution → Plugin Versions
- Click Add Plugin Version
- 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)
- Click Save
Step 4: Release the Version
- Edit the PluginVersion you just created
- Change Status to
released - Click Save
What happens: Signal triggers auto-build → ZIP created → database updated
Step 5: Verify Release
Run the verification checklist below.
Step 6: Deprecate Old Version (Optional)
- Find the previous version in Django admin
- Change Status to
deprecated - Save
5. Post-Update Verification Checklist
Immediate Checks (Do Every Release)
# 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
# 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)
- Go to a test WordPress site with plugin installed
- Navigate to Dashboard → Updates → Check Again
- Verify "IGNY8 WordPress Bridge" shows update available
- Click "View version details" → verify changelog appears
- 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
# 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:
# 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
- Set
force_update = Trueon the new PluginVersion - This flag signals WordPress sites that update is mandatory
- Sites with auto-updates enabled will update immediately
Emergency Release Checklist
# 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
# 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
# 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 - Full integration guide
- docs/plans/PLUGIN-DISTRIBUTION-SYSTEM.md - Original system design