Plugin packaging and docs
This commit is contained in:
424
docs/60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md
Normal file
424
docs/60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user