Skip to content

Commit 8efb92b

Browse files
committed
Fix #28 - Améliore la gestion des versions dans le vérificateur de mises à jour et ajoute la prise en charge des formats de version SemVer.
LIRE le fichier : Auto_update_documentation.md
1 parent 83aa3da commit 8efb92b

3 files changed

Lines changed: 230 additions & 19 deletions

File tree

Auto_update_documentation.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Auto-Update System for PiwigoPublish Plugin
2+
3+
By Gotcha26 - contact@julien-moreau.fr
4+
5+
## Overview
6+
7+
The auto-update system allows the PiwigoPublish Lightroom plugin to check for new versions via the GitHub Releases API and notify users when updates are available.
8+
9+
## Features
10+
11+
- **Automatic check on startup**: Silent check when Lightroom loads (once per day)
12+
- **Manual check**: Button in Plugin Manager to check on demand
13+
- **Multi-format versioning**: Supports both date-based and SemVer formats
14+
- **Cross-format comparison**: Can compare versions even when switching versioning schemes
15+
- **User-friendly notifications**: Dialog with changelog and download link
16+
17+
## Version Format Support
18+
19+
| Format | Example | Use Case |
20+
|--------|---------|----------|
21+
| Date-based | `20260121.3` or `v20260121.3` | Current format, revision incremented daily |
22+
| SemVer | `1.2.3` or `v1.2.3` | Industry standard, for future migration |
23+
24+
The system automatically detects which format is used and applies the appropriate comparison logic.
25+
26+
### Cross-Format Comparison
27+
28+
When the local and remote versions use different formats, the system falls back to comparing the **GitHub release publish date** (`published_at` metadata) against the **build date** embedded in the local version.
29+
30+
This ensures seamless updates even if the versioning scheme changes in the future.
31+
32+
## Configuration
33+
34+
In `UpdateChecker.lua`, the following constants can be adjusted:
35+
36+
```lua
37+
UpdateChecker.GITHUB_OWNER = "Piwigo" -- GitHub organization/user
38+
UpdateChecker.GITHUB_REPO = "PiwigoPublish-lrc-plugin" -- Repository name
39+
UpdateChecker.CHECK_INTERVAL_DAYS = 1 -- Days between automatic checks
40+
```
41+
42+
## User Interface
43+
44+
### Plugin Manager Section
45+
46+
A new "Plugin Updates" section appears in **File > Plug-in Manager > Piwigo Publisher**:
47+
48+
- Current version display
49+
- Update status indicator
50+
- "Check for Updates" button
51+
- "Visit GitHub Repository" button
52+
53+
### Update Notification Dialog
54+
55+
When an update is available, users see:
56+
57+
- Current vs. new version comparison
58+
- Changelog excerpt (first 500 characters)
59+
- "Download" button → opens GitHub release page
60+
- "Later" button → dismisses until next check
61+
62+
## Creating a New Release
63+
64+
1. Update `VERSION` in `Info.lua`:
65+
```lua
66+
VERSION = { major=20260122, minor=1, revision=0 },
67+
```
68+
69+
2. Commit and push changes
70+
71+
3. Create a GitHub Release:
72+
- **Tag**: `v20260122.1` (or `v1.0.0` for SemVer)
73+
- **Title**: Version number or descriptive title
74+
- **Description**: Changelog in Markdown format
75+
76+
4. The plugin will automatically detect the new release
77+
78+
## Technical Details
79+
80+
### API Endpoint
81+
82+
```
83+
GET https://api.github.com/repos/{owner}/{repo}/releases/latest
84+
```
85+
86+
### Response Fields Used
87+
88+
| Field | Purpose |
89+
|-------|---------|
90+
| `tag_name` | Version identifier |
91+
| `published_at` | Release date (ISO 8601) for cross-format comparison |
92+
| `body` | Changelog content (Markdown) |
93+
| `html_url` | Download page URL |
94+
95+
### Storage (LrPrefs)
96+
97+
| Key | Purpose |
98+
|-----|---------|
99+
| `lastUpdateCheck` | Timestamp of last check |
100+
| `latestVersion` | Cached latest version string |
101+
| `latestVersionUrl` | Cached download URL |
102+
| `pluginBuildDate` | Build date for SemVer installations |
103+
104+
## Functions Reference
105+
106+
| Function | Description |
107+
|----------|-------------|
108+
| `parseVersion(versionStr)` | Converts version string to comparable number |
109+
| `parseGitHubDate(dateStr)` | Parses ISO 8601 date to timestamp |
110+
| `getInstalledVersionDate()` | Extracts build date from installed version |
111+
| `shouldCheckForUpdates()` | Checks if interval has elapsed |
112+
| `checkForUpdates(silent)` | Main update check logic |
113+
| `openDownloadPage(url)` | Opens browser to download page |
114+
| `getUpdateStatus()` | Returns status string for UI |
115+
116+
## Future Considerations
117+
118+
- **Migration to SemVer**: The system is ready for a versioning scheme change without breaking update detection
119+
- **Pre-release support**: Could be extended to check for beta/RC releases via `prerelease` flag
120+
- **Auto-download**: Could be enhanced to download and extract updates automatically (requires additional permissions)
121+
122+
## Files Modified
123+
124+
| File | Changes |
125+
|------|---------|
126+
| `UpdateChecker.lua` | New file - update checking logic |
127+
| `Init.lua` | Added require and startup check |
128+
| `PluginInfoDialogSections.lua` | Added "Plugin Updates" UI section |
129+
| `Info.lua` | VERSION table (existing, used as source of truth) |

piwigoPublish.lrplugin/Init.lua

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ end
7373
_G.PiwigoBusy = false
7474
_G.RenderPhotos = false
7575
_G.iconPath = _PLUGIN:resourceId("icons/icon_med.png")
76-
_G.pluginVersion = "20260111.26"
76+
77+
-- Build version string from Info.lua VERSION table
78+
local versionInfo = _PLUGIN.VERSION or { major = 0, minor = 0, revision = 0 }
79+
_G.pluginVersion = string.format("%d.%d", versionInfo.major, versionInfo.minor)
7780
-- Auto-update checker
7881
_G.UpdateChecker = require "UpdateChecker"
7982

piwigoPublish.lrplugin/UpdateChecker.lua

Lines changed: 97 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Checks GitHub Releases API for new versions
66
77
Copyright (C) 2024 Fiona Boston <fiona@fbphotography.uk>.
8-
Copyright (C) 2026 Julien Moreau <contact@julien-moreau.fr>.
8+
Copyright (C) 2026 Julien Moreau
99
1010
This file is part of PiwigoPublish
1111
@@ -37,21 +37,69 @@ UpdateChecker.CHECK_INTERVAL_DAYS = 1
3737

3838
-- *************************************************
3939
function UpdateChecker.parseVersion(versionStr)
40-
-- Converts version string like "20260111.26" to comparable number
41-
-- Format: YYYYMMDD.revision
40+
-- Converts version strings to comparable numbers
41+
-- Supports two formats:
42+
-- Date-based: "20260111.26" or "v20260111.26" → YYYYMMDD * 1000 + revision
43+
-- SemVer: "1.2.3" or "v1.2.3" → major * 1000000 + minor * 1000 + patch
44+
4245
if not versionStr or versionStr == "" then
43-
return 0
46+
return 0, "unknown"
4447
end
4548

4649
-- Remove 'v' prefix if present
47-
versionStr = versionStr:gsub("^[vV]", "")
50+
versionStr = tostring(versionStr):gsub("^[vV]", "")
51+
52+
-- Check if it's date-based (starts with 20xx) or SemVer
53+
local firstPart = versionStr:match("^(%d+)")
54+
if not firstPart then
55+
return 0, "unknown"
56+
end
57+
58+
if tonumber(firstPart) >= 20000000 then
59+
-- Date-based format: YYYYMMDD.revision
60+
local major, minor = versionStr:match("^(%d+)%.?(%d*)")
61+
major = tonumber(major) or 0
62+
minor = tonumber(minor) or 0
63+
return major * 1000 + minor, "date"
64+
else
65+
-- SemVer format: major.minor.patch
66+
local major, minor, patch = versionStr:match("^(%d+)%.?(%d*)%.?(%d*)")
67+
major = tonumber(major) or 0
68+
minor = tonumber(minor) or 0
69+
patch = tonumber(patch) or 0
70+
return major * 1000000 + minor * 1000 + patch, "semver"
71+
end
72+
end
73+
74+
-- *************************************************
75+
function UpdateChecker.parseGitHubDate(dateStr)
76+
-- Parses GitHub ISO 8601 date (e.g., "2026-01-21T15:30:00Z") to timestamp
77+
if not dateStr then return 0 end
78+
79+
local year, month, day, hour, min, sec = dateStr:match("(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)")
80+
if not year then return 0 end
81+
82+
-- Convert to comparable number: YYYYMMDDHHMMSS
83+
return tonumber(string.format("%04d%02d%02d%02d%02d%02d",
84+
year, month, day, hour, min, sec)) or 0
85+
end
86+
87+
-- *************************************************
88+
function UpdateChecker.getInstalledVersionDate()
89+
-- Returns the install/build date from the version info
90+
-- For date-based: extracts YYYYMMDD from "20260111.26"
91+
-- For semver: uses a stored build date or returns 0
92+
93+
local versionInfo = _PLUGIN.VERSION or { major = 0, minor = 0, revision = 0 }
94+
local major = versionInfo.major or 0
4895

49-
local major, minor = versionStr:match("^(%d+)%.?(%d*)")
50-
if major then
51-
minor = minor or "0"
52-
return tonumber(major) * 1000 + tonumber(minor)
96+
if major >= 20000000 then
97+
-- Date-based: major IS the date
98+
return major
99+
else
100+
-- SemVer: check if we have a build date stored
101+
return prefs.pluginBuildDate or 0
53102
end
54-
return 0
55103
end
56104

57105
-- *************************************************
@@ -120,6 +168,8 @@ function UpdateChecker.checkForUpdates(silent)
120168

121169
-- Extract version info
122170
local remoteVersion = data.tag_name
171+
local publishedAt = data.published_at
172+
123173
if not remoteVersion then
124174
log:info("UpdateChecker.checkForUpdates - no tag_name in response")
125175
if not silent then
@@ -133,14 +183,41 @@ function UpdateChecker.checkForUpdates(silent)
133183
end
134184

135185
log:info("UpdateChecker.checkForUpdates - remote version: " .. remoteVersion)
186+
log:info("UpdateChecker.checkForUpdates - published_at: " .. tostring(publishedAt))
136187
log:info("UpdateChecker.checkForUpdates - current version: " .. pluginVersion)
137188

138-
local currentNum = UpdateChecker.parseVersion(pluginVersion)
139-
local remoteNum = UpdateChecker.parseVersion(remoteVersion)
189+
-- Determine comparison method based on version formats
190+
local currentNum, currentFormat = UpdateChecker.parseVersion(pluginVersion)
191+
local remoteNum, remoteFormat = UpdateChecker.parseVersion(remoteVersion)
140192

141-
log:info("UpdateChecker.checkForUpdates - currentNum: " .. currentNum .. ", remoteNum: " .. remoteNum)
193+
log:info("UpdateChecker.checkForUpdates - currentNum: " .. currentNum .. " (" .. currentFormat .. ")")
194+
log:info("UpdateChecker.checkForUpdates - remoteNum: " .. remoteNum .. " (" .. remoteFormat .. ")")
142195

143-
if remoteNum > currentNum then
196+
local updateAvailable = false
197+
198+
if currentFormat == remoteFormat then
199+
-- Same format: direct comparison
200+
updateAvailable = (remoteNum > currentNum)
201+
log:info("UpdateChecker.checkForUpdates - same format comparison: " .. tostring(updateAvailable))
202+
else
203+
-- Different formats: compare by GitHub publish date vs installed version date
204+
local remoteDate = UpdateChecker.parseGitHubDate(publishedAt)
205+
local installedDate = UpdateChecker.getInstalledVersionDate()
206+
207+
log:info("UpdateChecker.checkForUpdates - cross-format: remoteDate=" .. remoteDate .. ", installedDate=" .. installedDate)
208+
209+
-- If we can't determine installed date, fall back to assuming update is available
210+
if installedDate == 0 then
211+
updateAvailable = true
212+
log:info("UpdateChecker.checkForUpdates - unknown install date, assuming update available")
213+
else
214+
-- Compare: remoteDate is YYYYMMDDHHMMSS, installedDate is YYYYMMDD
215+
-- Normalize installedDate to same format (assume 00:00:00)
216+
updateAvailable = (remoteDate > installedDate * 1000000)
217+
end
218+
end
219+
220+
if updateAvailable then
144221
-- New version available
145222
local changelog = data.body or "No changelog available."
146223
-- Truncate changelog if too long
@@ -210,13 +287,15 @@ end
210287
function UpdateChecker.getUpdateStatus()
211288
-- Returns a status string for display in Plugin Manager
212289
local latestVersion = prefs.latestVersion
213-
local currentNum = UpdateChecker.parseVersion(pluginVersion)
214-
local latestNum = UpdateChecker.parseVersion(latestVersion or "")
290+
local currentNum, currentFormat = UpdateChecker.parseVersion(pluginVersion)
291+
local latestNum, latestFormat = UpdateChecker.parseVersion(latestVersion or "")
215292

216-
if latestNum > currentNum then
293+
if latestVersion and latestNum > currentNum and currentFormat == latestFormat then
217294
return string.format("Update available: %s", latestVersion)
218-
else
295+
elseif latestVersion then
219296
return "Up to date"
297+
else
298+
return "Not checked yet"
220299
end
221300
end
222301

0 commit comments

Comments
 (0)