Skip to content

Commit de63c63

Browse files
hotlongCopilot
andcommitted
feat(one): tauri auto-update with signed release manifest
- Embed tauri-plugin-updater v2; check on launch + tray menu item - Frontend banner listens for objectos://update-available, calls install_update - CI builds .tar.gz/.zip updater bundles + .sig artifacts - New scripts/build-update-manifest.mjs assembles latest.json from per-platform artifacts and uploads it alongside installers, served via https://github.com/objectstack-ai/objectos/releases/latest/download/latest.json Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f1e92cd commit de63c63

8 files changed

Lines changed: 736 additions & 17 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Build a Tauri updater manifest (latest.json) from CI artifacts.
4+
*
5+
* Walks the artifacts/ directory produced by `actions/download-artifact`,
6+
* finds the platform-specific updater bundles + their `.sig` siblings,
7+
* and emits a JSON manifest to stdout.
8+
*
9+
* Mapping (Tauri v2 default formats with createUpdaterArtifacts=true):
10+
* macOS → *.app.tar.gz (+ .sig)
11+
* Linux → *.AppImage.tar.gz | *.AppImage (+ .sig)
12+
* Windows → *.nsis.zip | *-setup.exe (+ .sig)
13+
*
14+
* Usage:
15+
* node build-update-manifest.mjs <artifacts-dir> <tag>
16+
*
17+
* <tag> is the git tag (e.g. `one-v6.0.0`). The leading `one-v` (or any
18+
* leading non-digit) is stripped to derive the semver.
19+
*
20+
* Public download URLs are built from the repo's GitHub Releases.
21+
*/
22+
import { readdirSync, readFileSync, statSync } from 'node:fs';
23+
import { join, basename } from 'node:path';
24+
25+
const [, , artifactsDir, tag] = process.argv;
26+
if (!artifactsDir || !tag) {
27+
console.error('usage: build-update-manifest.mjs <artifacts-dir> <tag>');
28+
process.exit(2);
29+
}
30+
31+
const REPO = process.env.GITHUB_REPOSITORY ?? 'objectstack-ai/objectos';
32+
const version = tag.replace(/^[^0-9]*/, '');
33+
34+
function* walk(dir) {
35+
for (const entry of readdirSync(dir)) {
36+
const p = join(dir, entry);
37+
const s = statSync(p);
38+
if (s.isDirectory()) yield* walk(p);
39+
else yield p;
40+
}
41+
}
42+
43+
const files = Array.from(walk(artifactsDir));
44+
const pick = (predicate) => files.find(predicate);
45+
46+
function platformEntry(bundle) {
47+
if (!bundle) return null;
48+
const sig = pick((f) => f === `${bundle}.sig`);
49+
if (!sig) {
50+
console.error(`! no .sig for ${bundle} — skipping`);
51+
return null;
52+
}
53+
return {
54+
signature: readFileSync(sig, 'utf8').trim(),
55+
url: `https://github.com/${REPO}/releases/download/${tag}/${basename(bundle)}`,
56+
};
57+
}
58+
59+
// Prefer updater archive; fall back to installer with sig alongside.
60+
const macArm =
61+
pick((f) => /aarch64.*\.app\.tar\.gz$/.test(f)) ??
62+
pick((f) => /arm64.*\.app\.tar\.gz$/.test(f));
63+
const macX64 =
64+
pick((f) => /x86_64.*\.app\.tar\.gz$/.test(f)) ??
65+
pick((f) => /x64.*\.app\.tar\.gz$/.test(f));
66+
const winX64 =
67+
pick((f) => /\.nsis\.zip$/.test(f)) ??
68+
pick((f) => /-setup\.exe$/.test(f) && !!pick((g) => g === `${f}.sig`));
69+
const linX64 =
70+
pick((f) => /\.AppImage\.tar\.gz$/.test(f)) ??
71+
pick((f) => /\.AppImage$/.test(f) && !!pick((g) => g === `${f}.sig`));
72+
73+
const platforms = {};
74+
const addIfFound = (key, bundle) => {
75+
const entry = platformEntry(bundle);
76+
if (entry) platforms[key] = entry;
77+
};
78+
addIfFound('darwin-aarch64', macArm);
79+
addIfFound('darwin-x86_64', macX64);
80+
addIfFound('windows-x86_64', winX64);
81+
addIfFound('linux-x86_64', linX64);
82+
83+
if (Object.keys(platforms).length === 0) {
84+
console.error('! no platform bundles detected — aborting');
85+
process.exit(1);
86+
}
87+
88+
const manifest = {
89+
version,
90+
notes: `See https://github.com/${REPO}/releases/tag/${tag}`,
91+
pub_date: new Date().toISOString(),
92+
platforms,
93+
};
94+
95+
process.stdout.write(JSON.stringify(manifest, null, 2) + '\n');
96+
console.error(`✓ manifest for v${version} with ${Object.keys(platforms).length} platforms`);

.github/workflows/one.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ jobs:
9898
apps/objectos-one/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.msi
9999
apps/objectos-one/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.deb
100100
apps/objectos-one/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.AppImage
101+
apps/objectos-one/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.tar.gz
102+
apps/objectos-one/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.zip
101103
apps/objectos-one/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.sig
102104
if-no-files-found: warn
103105

@@ -107,13 +109,27 @@ jobs:
107109
if: startsWith(github.ref, 'refs/tags/one-v') || inputs.release
108110
runs-on: ubuntu-22.04
109111
steps:
112+
- uses: actions/checkout@v4
110113
- uses: actions/download-artifact@v4
111114
with:
112115
path: artifacts
116+
- name: Generate latest.json (updater manifest)
117+
env:
118+
TAG: ${{ github.ref_name }}
119+
run: node .github/scripts/build-update-manifest.mjs artifacts "$TAG" > artifacts/latest.json
113120
- name: Create draft release
114121
uses: softprops/action-gh-release@v2
115122
with:
116123
draft: true
117124
generate_release_notes: true
118125
name: ObjectOS One ${{ github.ref_name }}
119-
files: artifacts/**/*
126+
files: |
127+
artifacts/**/*.dmg
128+
artifacts/**/*.exe
129+
artifacts/**/*.msi
130+
artifacts/**/*.deb
131+
artifacts/**/*.AppImage
132+
artifacts/**/*.tar.gz
133+
artifacts/**/*.zip
134+
artifacts/**/*.sig
135+
artifacts/latest.json

0 commit comments

Comments
 (0)