Skip to content

Commit ce4cb24

Browse files
hemanandrclaude
andcommitted
feat: implement manual update check JSON contract
- Update docs/updates.md with static JSON contract specification * Simple {current, latest, notes_url} format for offline-safe updates * Complete JSON schema with SemVer validation patterns * Implementation examples for manual update checking * Hosting recommendations (GitHub Pages, CDN, static sites) - Add sample update check JSON files in ops/examples/ * updates.json - standard update available scenario * updates-beta.json - pre-release version example * updates-no-update.json - up-to-date scenario * README.md with usage instructions and automation tips - Update OpenAPI specification (docs/openapi.yaml) * Revised /api/updates/latest endpoint to use UpdateCheck schema * Simplified response format aligned with static JSON contract * Updated examples to reflect new {current, latest, notes_url} format Addresses ENV-16 requirements for manual-only update checking with static JSON contract for offline-safe deployments. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 6fc82f8 commit ce4cb24

File tree

6 files changed

+169
-59
lines changed

6 files changed

+169
-59
lines changed

docs/openapi.yaml

Lines changed: 29 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -547,37 +547,34 @@ paths:
547547
# Update Management
548548
/api/updates/latest:
549549
get:
550-
summary: Check for software updates
550+
summary: Get update check information
551551
description: >
552-
Checks for available software updates by comparing current version with
553-
latest release. Manual update checking only - no automatic updates.
554-
operationId: checkUpdates
552+
Returns static update check information following the manual update JSON contract.
553+
Contains current version, latest available version, and release notes URL.
554+
Manual checking only - no automatic updates or downloads.
555+
operationId: getUpdateInfo
555556
tags:
556557
- Updates
557558
responses:
558559
"200":
559-
description: Update information retrieved successfully
560+
description: Update check information retrieved successfully
560561
content:
561562
application/json:
562563
schema:
563-
$ref: "#/components/schemas/UpdateInfo"
564+
$ref: "#/components/schemas/UpdateCheck"
564565
examples:
565566
update_available:
566567
summary: Update available
567568
value:
568-
current_version: "0.1.0"
569-
latest_version: "0.2.0"
570-
update_available: true
571-
release_notes_url: "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v0.2.0"
572-
download_url: "https://github.com/MachDatum/ThingConnect.Pulse/releases/download/v0.2.0/ThingConnect.Pulse.Setup.exe"
569+
current: "1.0.0"
570+
latest: "1.2.1"
571+
notes_url: "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v1.2.1"
573572
up_to_date:
574573
summary: Current version is latest
575574
value:
576-
current_version: "0.1.0"
577-
latest_version: "0.1.0"
578-
update_available: false
579-
release_notes_url: null
580-
download_url: null
575+
current: "1.2.1"
576+
latest: "1.2.1"
577+
notes_url: "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v1.2.1"
581578
"500":
582579
$ref: "#/components/responses/InternalError"
583580

@@ -1069,40 +1066,29 @@ components:
10691066
- errors
10701067

10711068
# Update Management
1072-
UpdateInfo:
1069+
UpdateCheck:
10731070
type: object
1074-
description: Software update information
1071+
description: Update check JSON contract for manual update checking
10751072
properties:
1076-
current_version:
1073+
current:
10771074
type: string
1078-
pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9.-]+)?$"
1079-
description: Current installed version (SemVer)
1080-
example: "0.1.0"
1081-
latest_version:
1075+
pattern: "^\\d+\\.\\d+\\.\\d+(-.+)?$"
1076+
description: Current installed version in SemVer format
1077+
example: "1.0.0"
1078+
latest:
10821079
type: string
1083-
pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9.-]+)?$"
1084-
description: Latest available version (SemVer)
1085-
example: "0.2.0"
1086-
update_available:
1087-
type: boolean
1088-
description: Whether an update is available
1089-
example: true
1090-
release_notes_url:
1091-
type: string
1092-
format: uri
1093-
nullable: true
1094-
description: URL to release notes for latest version
1095-
example: "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v0.2.0"
1096-
download_url:
1080+
pattern: "^\\d+\\.\\d+\\.\\d+(-.+)?$"
1081+
description: Latest available version in SemVer format
1082+
example: "1.2.1"
1083+
notes_url:
10971084
type: string
10981085
format: uri
1099-
nullable: true
1100-
description: URL to download latest version installer
1101-
example: "https://github.com/MachDatum/ThingConnect.Pulse/releases/download/v0.2.0/ThingConnect.Pulse.Setup.exe"
1086+
description: URL to release notes for the latest version
1087+
example: "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v1.2.1"
11021088
required:
1103-
- current_version
1104-
- latest_version
1105-
- update_available
1089+
- current
1090+
- latest
1091+
- notes_url
11061092

11071093
# Error Response
11081094
ErrorResponse:

docs/updates.md

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -63,50 +63,103 @@ graph TD
6363

6464
## Update Detection & Notification
6565

66-
### GitHub Release Integration
66+
### Manual Update Check JSON Contract
6767

68-
**Release API Endpoint**:
68+
ThingConnect Pulse uses a **static JSON contract** for manual update checks, designed to be offline-safe and simple to implement.
69+
70+
**Update Check JSON Schema**:
71+
```json
72+
{
73+
"$schema": "http://json-schema.org/draft-07/schema#",
74+
"title": "ThingConnect Pulse Update Check",
75+
"type": "object",
76+
"required": ["current", "latest", "notes_url"],
77+
"properties": {
78+
"current": {
79+
"type": "string",
80+
"pattern": "^\\d+\\.\\d+\\.\\d+(-.+)?$",
81+
"description": "Current application version in SemVer format"
82+
},
83+
"latest": {
84+
"type": "string",
85+
"pattern": "^\\d+\\.\\d+\\.\\d+(-.+)?$",
86+
"description": "Latest available version in SemVer format"
87+
},
88+
"notes_url": {
89+
"type": "string",
90+
"format": "uri",
91+
"description": "URL to release notes for the latest version"
92+
}
93+
}
94+
}
6995
```
70-
GET https://api.github.com/repos/MachDatum/ThingConnect.Pulse/releases/latest
96+
97+
**Example Update Check Response**:
98+
```json
99+
{
100+
"current": "1.0.0",
101+
"latest": "1.2.1",
102+
"notes_url": "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v1.2.1"
103+
}
71104
```
72105

73-
**Response Processing**:
106+
### Update Check Implementation
107+
108+
**Manual Update Check Service**:
74109
```csharp
75110
public sealed class UpdateService : IUpdateService
76111
{
77-
public async Task<UpdateInfo> CheckForUpdatesAsync()
112+
private readonly HttpClient _httpClient;
113+
private readonly ILogger<UpdateService> _logger;
114+
115+
public async Task<UpdateInfo> CheckForUpdatesAsync(string updateJsonUrl)
78116
{
79117
try
80118
{
81-
using var client = new HttpClient();
82-
client.DefaultRequestHeaders.Add("User-Agent", "ThingConnect.Pulse/1.0.0");
119+
_httpClient.DefaultRequestHeaders.Add("User-Agent", "ThingConnect.Pulse/1.0.0");
83120

84-
var response = await client.GetStringAsync(
85-
"https://api.github.com/repos/MachDatum/ThingConnect.Pulse/releases/latest");
86-
var release = JsonSerializer.Deserialize<GitHubRelease>(response);
121+
var response = await _httpClient.GetStringAsync(updateJsonUrl);
122+
var updateCheck = JsonSerializer.Deserialize<UpdateCheckResponse>(response);
87123

88-
var latestVersion = Version.Parse(release.TagName.TrimStart('v'));
89-
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version;
124+
var latestVersion = Version.Parse(updateCheck.Latest);
125+
var currentVersion = Version.Parse(updateCheck.Current);
90126

91127
return new UpdateInfo
92128
{
93129
HasUpdate = latestVersion > currentVersion,
94130
LatestVersion = latestVersion,
95131
CurrentVersion = currentVersion,
96-
ReleaseNotes = release.Body,
97-
DownloadUrl = release.Assets.FirstOrDefault()?.BrowserDownloadUrl,
98-
PublishedAt = release.PublishedAt
132+
NotesUrl = updateCheck.NotesUrl,
133+
UpdateAvailable = latestVersion > currentVersion
99134
};
100135
}
101136
catch (Exception ex)
102137
{
103-
_logger.LogWarning(ex, "Failed to check for updates");
138+
_logger.LogWarning(ex, "Failed to check for updates from {Url}", updateJsonUrl);
104139
return UpdateInfo.CheckFailed();
105140
}
106141
}
107142
}
143+
144+
public record UpdateCheckResponse(
145+
string Current,
146+
string Latest,
147+
string NotesUrl
148+
);
108149
```
109150

151+
### Static JSON Hosting
152+
153+
**GitHub Pages Hosting** (Recommended):
154+
- Host `updates.json` in the repository's `gh-pages` branch
155+
- Accessible via: `https://machDatum.github.io/ThingConnect.Pulse/updates.json`
156+
- Automatically updated via GitHub Actions on new releases
157+
158+
**Alternative Hosting Options**:
159+
- **CDN**: CloudFlare, AWS CloudFront, or Azure CDN for global distribution
160+
- **Static hosting**: Netlify, Vercel, or similar services
161+
- **Enterprise**: Internal web servers for air-gapped environments
162+
110163
### Update Check Configuration
111164

112165
**Settings Options**:

ops/examples/README.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Example Files
2+
3+
This directory contains example files for ThingConnect Pulse operations and configuration.
4+
5+
## Update Check JSON Examples
6+
7+
### updates.json
8+
Standard update check response showing an available update from version 1.0.0 to 1.2.1.
9+
10+
**Usage**: Host this file on a web server and configure ThingConnect Pulse to check this URL for updates.
11+
12+
### updates-beta.json
13+
Beta channel update check response showing a pre-release version (1.3.0-beta.2) available.
14+
15+
**Usage**: Used for testing beta releases and pre-release update notifications.
16+
17+
### updates-no-update.json
18+
Response when no update is available (current version matches latest version).
19+
20+
**Usage**: Shows the expected format when the system is up-to-date.
21+
22+
## Update Check JSON Contract
23+
24+
All update check JSON files must follow this structure:
25+
26+
```json
27+
{
28+
"current": "1.0.0", // Current installed version (SemVer)
29+
"latest": "1.2.1", // Latest available version (SemVer)
30+
"notes_url": "https://..." // URL to release notes/changelog
31+
}
32+
```
33+
34+
### Field Requirements
35+
36+
- **current**: Must be a valid semantic version (MAJOR.MINOR.PATCH)
37+
- **latest**: Must be a valid semantic version, may include pre-release identifiers
38+
- **notes_url**: Must be a valid HTTP/HTTPS URL pointing to release documentation
39+
40+
### Hosting Recommendations
41+
42+
1. **GitHub Pages**: Host in `gh-pages` branch for automatic updates
43+
2. **CDN**: Use CloudFlare or AWS CloudFront for global distribution
44+
3. **Static Site**: Deploy to Netlify, Vercel, or similar services
45+
4. **Enterprise**: Internal web servers for air-gapped environments
46+
47+
### Automation
48+
49+
Consider automating the update of these JSON files when new releases are published:
50+
51+
```yaml
52+
# GitHub Actions example
53+
- name: Update JSON files
54+
run: |
55+
echo '{"current":"${{ env.PREVIOUS_VERSION }}","latest":"${{ env.NEW_VERSION }}","notes_url":"${{ env.RELEASE_URL }}"}' > updates.json
56+
```

ops/examples/updates-beta.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"current": "1.0.0",
3+
"latest": "1.3.0-beta.2",
4+
"notes_url": "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v1.3.0-beta.2"
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"current": "1.2.1",
3+
"latest": "1.2.1",
4+
"notes_url": "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v1.2.1"
5+
}

ops/examples/updates.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"current": "1.0.0",
3+
"latest": "1.2.1",
4+
"notes_url": "https://github.com/MachDatum/ThingConnect.Pulse/releases/tag/v1.2.1"
5+
}

0 commit comments

Comments
 (0)