Files
igny8/docs/60-PLUGINS/PLUGIN-UPDATE-WORKFLOW.md
IGNY8 VPS (Salman) c777e5ccb2 dos updates
2026-01-20 14:45:21 +00:00

25 KiB

Plugin Update Workflow

Last Updated: January 20, 2026
Version: 1.8.4
Status: Production
Scope: How to release plugin updates and what happens automatically vs manually


🎯 Quick Start: Simplified Release Process

The plugin release process has been simplified to require only 3 fields:

  1. Version (e.g., 1.3.3)
  2. Changelog (what's new)
  3. Status (draft → released)

All other fields are either:

  • Auto-filled from previous version
  • Auto-generated on release (file path, size, checksum)
  • Auto-calculated (version code)

Release in 3 clicks:

Django Admin → Add Plugin Version → Enter 3 fields → Save → Change status to 'released'

Recent Release History (v1.7.0)

WordPress Plugin Progression

Version Date Type Key Changes
1.5.1 Jan 10, 2026 Minor Admin UI consolidation: 6→3 pages (Dashboard, Settings, Logs), header alignment fixes
1.5.0 Jan 10, 2026 Minor Authentication simplification: Site.wp_api_key as single source of truth
1.3.3 Jan 10, 2026 Patch Template design: Square image grid fixes, landscape image positioning, direct border-radius/shadow on images without captions
1.3.2 Jan 9, 2026 Patch Template fixes in app and plugin, image layout improvements
1.3.1 Jan 9, 2026 Patch WordPress plugin versioning updates
1.3.0 Jan 8, 2026 Minor Initial distribution system release, automated updates

Distribution System Status

  • Automated ZIP generation: Operational
  • Checksum calculation: MD5 + SHA256
  • WordPress auto-update: Active
  • Health checks: Implemented
  • Installation tracking: Complete

Table of Contents

  1. Update Workflow Overview
  2. What Happens Automatically
  3. What Requires Manual Action
  4. Step-by-Step: Releasing a New Version
  5. Post-Update Verification Checklist
  6. Version Numbering
  7. Rollback Procedure
  8. Emergency Updates
  9. Recent Updates & Lessons Learned

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: just enter version, changelog, status
Verify After Release After status change Run verification checklist
Mark Old Version Deprecated After successful release Change old version status

Note: The form is simplified - most fields auto-fill from previous version or are auto-generated.

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

The Simplified Process

┌─────────────────────────────────────────────────────────────────────────┐
│              Simplified Version Release (3 Required Fields)              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                          │
│  Django Admin → Add Plugin Version                                      │
│                                                                          │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │  Plugin: [IGNY8 WordPress Bridge ▼]       ← Select plugin      │    │
│  │  Version: [1.2.0]                          ← New version        │    │
│  │  Status: [draft ▼]                         ← Start as draft     │    │
│  │  Changelog: [Bug fixes and improvements]   ← What's new         │    │
│  │                                                                  │    │
│  │  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━  │    │
│  │  Everything below is AUTO-FILLED or AUTO-GENERATED:             │    │
│  │                                                                  │    │
│  │  Min API Version: 1.0      (from previous version)              │    │
│  │  Min PHP Version: 7.4      (from previous version)              │    │
│  │  File Path: (empty)        → Auto-generated on release          │    │
│  │  File Size: (empty)        → Auto-calculated on release         │    │
│  │  Checksum: (empty)         → Auto-calculated on release         │    │
│  │  Version Code: (empty)     → Auto-calculated from version       │    │
│  └────────────────────────────────────────────────────────────────┘    │
│                                                                          │
│  Click [Save] → Status changes to 'released' → ZIP builds automatically │
│                                                                          │
└─────────────────────────────────────────────────────────────────────────┘

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

  1. Go to: https://api.igny8.com/backend/
  2. Navigate to: Plugin DistributionPlugin Versions
  3. Click Add Plugin Version
  4. Fill in ONLY these required fields:
    • Plugin: IGNY8 WordPress Bridge
    • Version: 1.2.0
    • Status: draft (initially)
    • Changelog: Describe changes
  5. Click Save

Note: All other fields are auto-filled:

  • min_api_version, min_platform_version, min_php_version → Copied from previous version
  • file_path, file_size, checksum → Auto-generated when you release
  • version_code → Auto-calculated from version number

Step 4: Release the Version

Option A: Release via Status Change

  1. Edit the PluginVersion you just created
  2. Change Status to released
  3. Click Save

Option B: Release via Bulk Action

  1. Select the version(s) in the list
  2. Choose Actions Release selected versions
  3. Click Go

What happens: Signal triggers auto-build → ZIP created → database updated with file info

Note: You can also use the action 📢 Mark as update ready to immediately notify WordPress sites.

Step 5: Verify Release

Run the 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

Quick Verification Script

Run this single command to verify everything:

#!/bin/bash
# Complete Plugin Release Verification Script
# Usage: Save as verify-plugin.sh and run: bash verify-plugin.sh 1.1.2

VERSION=${1:-"latest"}
PLUGIN_SLUG="igny8-wp-bridge"

echo "=========================================="
echo "Plugin Release Verification: v${VERSION}"
echo "=========================================="
echo ""

# 1. Check ZIP file exists
echo "1. Checking ZIP file..."
if [ "$VERSION" = "latest" ]; then
    ls -lh /data/app/igny8/plugins/wordpress/dist/${PLUGIN_SLUG}-latest.zip 2>/dev/null && echo "   ✓ Latest ZIP exists" || echo "   ✗ Latest ZIP not found"
else
    ls -lh /data/app/igny8/plugins/wordpress/dist/${PLUGIN_SLUG}-v${VERSION}.zip 2>/dev/null && echo "   ✓ ZIP v${VERSION} exists" || echo "   ✗ ZIP v${VERSION} not found"
fi
echo ""

# 2. Check symlink
echo "2. Checking symlink..."
ls -la /data/app/igny8/plugins/wordpress/dist/${PLUGIN_SLUG}-latest.zip | grep -q "^l" && echo "   ✓ Symlink valid" || echo "   ✗ Symlink missing"
echo ""

# 3. Test download endpoint
echo "3. Testing download endpoint..."
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" https://api.igny8.com/api/plugins/${PLUGIN_SLUG}/download/)
FILE_SIZE=$(curl -s -o /dev/null -w "%{size_download}" https://api.igny8.com/api/plugins/${PLUGIN_SLUG}/download/)
if [ "$HTTP_CODE" = "200" ]; then
    echo "   ✓ Download works: ${HTTP_CODE} - ${FILE_SIZE} bytes ($(( FILE_SIZE / 1024 )) KB)"
else
    echo "   ✗ Download failed: HTTP ${HTTP_CODE}"
fi
echo ""

# 4. Test check-update endpoint
echo "4. Testing check-update endpoint..."
UPDATE_RESPONSE=$(curl -s "https://api.igny8.com/api/plugins/${PLUGIN_SLUG}/check-update/?current_version=1.0.0")
LATEST_VERSION=$(echo "$UPDATE_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin).get('latest_version', 'N/A'))" 2>/dev/null)
if [ -n "$LATEST_VERSION" ] && [ "$LATEST_VERSION" != "N/A" ]; then
    echo "   ✓ Check-update works: Latest version = $LATEST_VERSION"
else
    echo "   ✗ Check-update failed"
fi
echo ""

# 5. Test info endpoint
echo "5. Testing info endpoint..."
INFO_RESPONSE=$(curl -s "https://api.igny8.com/api/plugins/${PLUGIN_SLUG}/info/")
PLUGIN_NAME=$(echo "$INFO_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin).get('name', 'N/A'))" 2>/dev/null)
if [ -n "$PLUGIN_NAME" ] && [ "$PLUGIN_NAME" != "N/A" ]; then
    echo "   ✓ Info endpoint works: $PLUGIN_NAME"
else
    echo "   ✗ Info endpoint failed"
fi
echo ""

# 6. Verify version in ZIP
echo "6. Verifying version in ZIP..."
if [ "$VERSION" != "latest" ]; then
    ZIP_VERSION=$(unzip -p /data/app/igny8/plugins/wordpress/dist/${PLUGIN_SLUG}-v${VERSION}.zip ${PLUGIN_SLUG}/igny8-bridge.php 2>/dev/null | grep -E "Version:" | head -1 | awk '{print $3}')
    if [ "$ZIP_VERSION" = "$VERSION" ]; then
        echo "   ✓ ZIP version matches: $ZIP_VERSION"
    else
        echo "   ✗ ZIP version mismatch: expected $VERSION, got $ZIP_VERSION"
    fi
else
    echo "   - Skipped (use specific version to check)"
fi
echo ""

# 7. Verify API URL
echo "7. Verifying API URL in ZIP..."
API_COUNT=$(unzip -p /data/app/igny8/plugins/wordpress/dist/${PLUGIN_SLUG}-latest.zip ${PLUGIN_SLUG}/igny8-bridge.php 2>/dev/null | grep -c "api.igny8.com")
if [ "$API_COUNT" -gt 0 ]; then
    echo "   ✓ API URL correct: api.igny8.com found (${API_COUNT} occurrences)"
else
    echo "   ✗ API URL incorrect: api.igny8.com not found"
fi
echo ""

# 8. Check database
echo "8. Checking database..."
docker exec igny8_backend python manage.py shell -c "
from igny8_core.plugins.models import Plugin, PluginVersion
try:
    p = Plugin.objects.get(slug='${PLUGIN_SLUG}')
    v = p.get_latest_version()
    if v:
        print(f'   ✓ Latest version: {v.version}')
        print(f'   ✓ Status: {v.status}')
        print(f'   ✓ File: {v.file_path}')
        print(f'   ✓ Size: {v.file_size:,} bytes ({v.file_size/1024:.1f} KB)')
        print(f'   ✓ Checksum: {v.checksum[:32]}...')
    else:
        print('   ✗ No released version found')
except Exception as e:
    print(f'   ✗ Error: {e}')
" 2>/dev/null | grep -E "✓|✗"
echo ""

echo "=========================================="
echo "Verification Complete"
echo "=========================================="

Quick run (copy-paste):

# Verify latest version
curl -s "https://api.igny8.com/api/plugins/igny8-wp-bridge/info/" | python3 -m json.tool && \
curl -s "https://api.igny8.com/api/plugins/igny8-wp-bridge/check-update/?current_version=1.0.0" | python3 -m json.tool && \
ls -lh /data/app/igny8/plugins/wordpress/dist/igny8-wp-bridge-latest.zip && \
echo "✓ All endpoints working"

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)

  1. Go to a test WordPress site with plugin installed
  2. Navigate to DashboardUpdatesCheck 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

# 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

  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

# 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

9. Recent Updates & Lessons Learned

v1.3.3 Release (Jan 10, 2026)

Changes:

  • Fixed square image grid layout (side-by-side display)
  • Fixed landscape image positioning in sections 4+
  • Removed card wrapper for images without captions
  • Applied border-radius/shadow directly to images
  • Landscape images now appear after first paragraph

Process:

  • Source code updated in plugin templates and CSS
  • Version bumped in igny8-bridge.php (header + constant)
  • Django admin: Created v1.3.3 with changelog
  • Status changed to "released" → ZIP auto-generated
  • Verified download endpoint and file contents
  • Template changes tested on staging WordPress site

Lessons:

  • CSS changes require thorough cross-browser testing
  • Image layout fixes need responsive design verification
  • Template changes should be tested with multiple content types

v1.3.2 Release (Jan 9, 2026)

Changes:

  • Template rendering improvements
  • Image layout enhancements
  • Content section fixes

Process:

  • Standard release workflow
  • Auto-build successful
  • No rollback needed

v1.3.0 Release (Jan 8, 2026)

Changes:

  • Initial distribution system implementation
  • WordPress auto-update mechanism
  • Base template system

Process:

  • Major release with new infrastructure
  • Extensive testing required
  • First use of automated packaging

Distribution System Milestones

v1.7.0 Infrastructure:

  • Complete plugin distribution system
  • Multi-platform support architecture
  • Automated versioning and packaging
  • Security features (checksums, signed URLs)
  • Monitoring and analytics

Best Practices Established:

  1. Always test download endpoint after release
  2. Verify ZIP contents match source
  3. Check version number in extracted files
  4. Test update notification in WordPress
  5. Monitor download analytics

Common Issues Resolved:

  • ZIP generation timing → Now synchronous in signals
  • Checksum mismatches → Auto-calculated reliably
  • Version comparison → Semantic versioning logic fixed
  • File size tracking → Automatic and accurate

Quick Reference Card

Release New Version (Simplified)

1. Edit source code in /plugins/wordpress/source/igny8-wp-bridge/
2. Update version in igny8-bridge.php (header + constant)
3. Django Admin → Add Plugin Version:
   - Plugin: IGNY8 WordPress Bridge
   - Version: 1.3.3 (or next version)
   - Changelog: Describe changes
   - Status: draft
   - (All other fields auto-fill)
4. Change status to "released" (or use bulk action) → Auto-build triggers
5. Run verification checklist
6. Optionally: Mark old version as deprecated

Admin Bulk Actions:

  • Release selected versions - Builds ZIP and marks as released
  • 📢 Mark as update ready - Notifies WordPress sites
  • 🗑️ Mark as deprecated - Deprecates old versions

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')
"