Skip to content

Commit c56443d

Browse files
committed
Update files
1 parent b18232f commit c56443d

14 files changed

Lines changed: 1910 additions & 411 deletions

File tree

.github/workflows/desktop.yml

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
name: desktop
2+
3+
on:
4+
push:
5+
tags:
6+
- 'desktop-v*'
7+
workflow_dispatch:
8+
inputs:
9+
release:
10+
description: 'Create a draft GitHub release with the artifacts'
11+
type: boolean
12+
default: false
13+
14+
permissions:
15+
contents: write
16+
17+
jobs:
18+
build:
19+
name: ${{ matrix.label }}
20+
runs-on: ${{ matrix.os }}
21+
strategy:
22+
fail-fast: false
23+
matrix:
24+
include:
25+
- label: macOS (arm64)
26+
os: macos-14
27+
target: aarch64-apple-darwin
28+
bundle-dir: macos
29+
- label: macOS (x64)
30+
os: macos-13
31+
target: x86_64-apple-darwin
32+
bundle-dir: macos
33+
- label: Windows (x64)
34+
os: windows-2022
35+
target: x86_64-pc-windows-msvc
36+
bundle-dir: nsis
37+
- label: Linux (x64)
38+
os: ubuntu-22.04
39+
target: x86_64-unknown-linux-gnu
40+
bundle-dir: deb
41+
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- name: Linux build deps
46+
if: runner.os == 'Linux'
47+
run: |
48+
sudo apt-get update
49+
sudo apt-get install -y \
50+
libwebkit2gtk-4.1-dev libssl-dev libayatana-appindicator3-dev \
51+
librsvg2-dev patchelf libgtk-3-dev libsoup-3.0-dev
52+
53+
- uses: pnpm/action-setup@v4
54+
with:
55+
version: 10.28.2
56+
57+
- uses: actions/setup-node@v4
58+
with:
59+
node-version: 20
60+
cache: pnpm
61+
62+
- uses: dtolnay/rust-toolchain@stable
63+
with:
64+
targets: ${{ matrix.target }}
65+
66+
- uses: Swatinem/rust-cache@v2
67+
with:
68+
workspaces: apps/objectos-desktop/src-tauri
69+
70+
- name: Install workspace deps
71+
run: pnpm install --frozen-lockfile
72+
73+
- name: Stage Node runtime
74+
run: pnpm --filter @objectos/desktop stage
75+
76+
- name: Build Tauri bundle
77+
env:
78+
# macOS signing (provide via repo secrets to enable notarization).
79+
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
80+
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
81+
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
82+
APPLE_ID: ${{ secrets.APPLE_ID }}
83+
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
84+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
85+
# Tauri updater key (optional).
86+
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
87+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
88+
run: pnpm --filter @objectos/desktop tauri build --target ${{ matrix.target }}
89+
90+
- name: Upload artifacts
91+
uses: actions/upload-artifact@v4
92+
with:
93+
name: objectos-desktop-${{ matrix.target }}
94+
path: |
95+
apps/objectos-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.dmg
96+
apps/objectos-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.app
97+
apps/objectos-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.exe
98+
apps/objectos-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.msi
99+
apps/objectos-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.deb
100+
apps/objectos-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.AppImage
101+
apps/objectos-desktop/src-tauri/target/${{ matrix.target }}/release/bundle/**/*.sig
102+
if-no-files-found: warn
103+
104+
release:
105+
name: Draft release
106+
needs: build
107+
if: startsWith(github.ref, 'refs/tags/desktop-v') || inputs.release
108+
runs-on: ubuntu-22.04
109+
steps:
110+
- uses: actions/download-artifact@v4
111+
with:
112+
path: artifacts
113+
- name: Create draft release
114+
uses: softprops/action-gh-release@v2
115+
with:
116+
draft: true
117+
generate_release_notes: true
118+
name: ObjectOS Desktop ${{ github.ref_name }}
119+
files: artifacts/**/*

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ docker/artifacts/*
1818
!docker/artifacts/.gitkeep
1919
.cache/
2020
apps/objectos-desktop/runtime/
21+
apps/objectos-desktop/src-tauri/runtime/
2122
apps/objectos-desktop/src-tauri/target/
2223
apps/objectos-desktop/src-tauri/gen/

apps/objectos-desktop/README.md

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,41 +11,60 @@ experience for end users on macOS, Windows and Linux.
1111
┌──────────────────────────────────────────┐
1212
│ Tauri shell (Rust) │
1313
│ ├── splash WebView (src/index.html) │
14+
│ ├── system tray (open / restart / data │
15+
│ │ folder / quit) │
1416
│ ├── waits for sidecar port │
1517
│ └── navigates to http://localhost:N │
1618
│ │
1719
│ Sidecar: bundled Node runs │
1820
│ apps/objectos/desktop.mjs │
1921
│ → objectstack serve --port N │
22+
│ → SQLite + uploads under │
23+
│ $OBJECTOS_HOME │
2024
└──────────────────────────────────────────┘
2125
```
2226

2327
The Node tree is staged under `runtime/` by
2428
`scripts/stage-runtime.mjs` (called automatically by `dev` / `build`)
25-
and bundled by Tauri as resources.
29+
and bundled by Tauri as resources. The staging step also slims the
30+
tree (~50 MB of source maps, markdown, and test fixtures removed).
31+
32+
## Per-user data
33+
34+
| OS | Path |
35+
|---------|----------------------------------------------|
36+
| macOS / Linux / Windows | `~/.objectstack` |
37+
38+
(Override with `OBJECTOS_HOME=/some/path`.)
39+
40+
The sidecar receives `OBJECTOS_HOME`, `OS_DATABASE_URL`,
41+
`OS_STORAGE_ROOT`, and `OS_CACHE_DIR` pointed inside that folder, so a
42+
clean uninstall is just deleting that directory.
2643

2744
## Prerequisites
2845

2946
- Node ≥ 20 + pnpm 10
3047
- Rust (stable) — `curl https://sh.rustup.rs -sSf | sh`
3148
- macOS: Xcode Command Line Tools
3249
- Windows: WebView2 (preinstalled on Win10+) + MSVC build tools
33-
- Linux: `libwebkit2gtk-4.1-dev`, `build-essential`, `libssl-dev`
50+
- Linux: `libwebkit2gtk-4.1-dev`, `build-essential`, `libssl-dev`,
51+
`libayatana-appindicator3-dev`, `librsvg2-dev`
3452

3553
## Develop
3654

3755
```bash
38-
pnpm install # repo root
39-
pnpm --filter @objectos/desktop dev # stages runtime + tauri dev
56+
pnpm install
57+
pnpm desktop:dev # = pnpm --filter @objectos/desktop dev
4058
```
4159

42-
The first run builds Rust dependencies (~2–4 min). Subsequent runs are
43-
fast.
60+
First run builds Rust dependencies (~2–4 min). Subsequent runs are
61+
fast. The window opens on a splash page; once the sidecar is ready it
62+
navigates to the live Studio URL.
4463

4564
## Build distributables
4665

4766
```bash
48-
pnpm --filter @objectos/desktop build
67+
pnpm desktop:build
4968
```
5069

5170
Output lands in `src-tauri/target/release/bundle/`:
@@ -56,15 +75,66 @@ Output lands in `src-tauri/target/release/bundle/`:
5675
| Windows | `nsis/ObjectOS_<v>_x64-setup.exe` |
5776
| Linux | `deb/objectos_<v>_amd64.deb`, AppImage |
5877

59-
Code signing / notarization is configured per platform in
60-
`src-tauri/tauri.conf.json` (see Tauri docs).
78+
## CI
79+
80+
`.github/workflows/desktop.yml` builds all four platforms in parallel
81+
(macOS arm64, macOS x64, Windows x64, Linux x64). Trigger:
82+
83+
- Push a tag matching `desktop-v*` → full build + draft GitHub release
84+
- Manual `workflow_dispatch` → artifacts only
85+
86+
The workflow is wired for code signing — provide the secrets below to
87+
enable. Without secrets the builds still succeed (unsigned binaries,
88+
end users will see OS warnings on first launch).
89+
90+
## Code signing
91+
92+
### macOS (Developer ID + notarization)
93+
94+
Required repo secrets:
95+
96+
| Secret | Value |
97+
|------------------------------|----------------------------------------------------|
98+
| `APPLE_CERTIFICATE` | base64 of your `.p12` Developer ID Application cert |
99+
| `APPLE_CERTIFICATE_PASSWORD` | the export password |
100+
| `APPLE_SIGNING_IDENTITY` | `Developer ID Application: Company (TEAMID)` |
101+
| `APPLE_ID` | Apple ID email used for notarization |
102+
| `APPLE_PASSWORD` | App‑specific password (appleid.apple.com → security) |
103+
| `APPLE_TEAM_ID` | 10‑char team ID from Apple Developer portal |
104+
105+
Tauri picks these up automatically and runs `codesign` + `notarytool`
106+
during `tauri build`.
107+
108+
### Windows (Authenticode)
109+
110+
| Secret | Value |
111+
|---------------------------------|----------------------|
112+
| `WINDOWS_CERTIFICATE` | base64 of your `.pfx`|
113+
| `WINDOWS_CERTIFICATE_PASSWORD` | export password |
114+
115+
Then in `tauri.conf.json` set `bundle.windows.certificateThumbprint`
116+
to the SHA‑1 thumbprint (or extend the workflow to import the .pfx
117+
and sign post‑build with `signtool`).
118+
119+
### Tauri updater key (optional)
120+
121+
To enable in‑app auto‑update:
122+
123+
```bash
124+
pnpm tauri signer generate -w ~/.tauri/objectos.key
125+
```
126+
127+
Set `TAURI_SIGNING_PRIVATE_KEY` (file contents) and
128+
`TAURI_SIGNING_PRIVATE_KEY_PASSWORD` in CI, paste the public key into
129+
`tauri.conf.json → plugins.updater.pubkey`, and flip
130+
`plugins.updater.active` to `true`.
61131

62132
## Comparison with the portable zip
63133

64134
| | Portable zip | Tauri |
65135
|---|---|---|
66136
| Brand | none (terminal) | dock icon, menu, tray |
67-
| Size | ~110 MB | ~120 MB (Node sidecar dominates; shell adds ~10 MB) |
137+
| Size | ~110 MB zip | ~140 MB installer (signed) |
68138
| Auto‑update | no | yes (Tauri updater) |
69139
| Code signing | manual | first‑class |
70140
| Dev effort | tiny | moderate (Rust toolchain) |

apps/objectos-desktop/scripts/stage-runtime.mjs

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
* node_modules/
1616
*/
1717
import { execSync } from 'node:child_process';
18-
import { existsSync, mkdirSync, copyFileSync, rmSync, chmodSync } from 'node:fs';
18+
import { existsSync, mkdirSync, copyFileSync, rmSync, chmodSync, readdirSync, statSync, unlinkSync } from 'node:fs';
1919
import { dirname, join, resolve } from 'node:path';
2020
import { fileURLToPath } from 'node:url';
2121
import { platform, arch } from 'node:os';
@@ -24,7 +24,7 @@ const HERE = dirname(fileURLToPath(import.meta.url));
2424
const PKG = resolve(HERE, '..');
2525
const REPO = resolve(PKG, '../..');
2626
const APP = resolve(REPO, 'apps/objectos');
27-
const RUNTIME = resolve(PKG, 'runtime');
27+
const RUNTIME = resolve(PKG, 'src-tauri/runtime');
2828
const NODE_VERSION = process.env.NODE_VERSION ?? '20.18.1';
2929

3030
const osName = ({ darwin: 'darwin', linux: 'linux', win32: 'win' })[platform()];
@@ -39,6 +39,74 @@ const sh = (cmd, opts = {}) => {
3939
execSync(cmd, { stdio: 'inherit', ...opts });
4040
};
4141

42+
/**
43+
* Strip files that are useless at runtime. Conservative — only deletes
44+
* patterns that are universally safe in npm packages:
45+
* * .map (source maps)
46+
* * .md / .markdown (docs)
47+
* * directories named __tests__, .github, .nyc_output, coverage
48+
* * cross-platform native prebuilds (we ship one platform per installer)
49+
*
50+
* NOTE: we deliberately do *not* touch directories called `doc`,
51+
* `docs`, `example`, `examples`, `test`, or `tests` — some packages
52+
* (e.g. `yaml`) ship real runtime code under those names.
53+
*/
54+
function slim(root) {
55+
if (!existsSync(root)) return;
56+
const dropFileExt = /\.(map|md|markdown)$/i;
57+
const dropDirName = /^(__tests__|\.github|\.nyc_output|coverage)$/i;
58+
const platformTag = `${platform() === 'win32' ? 'win32' : platform()}-${arch()}`;
59+
60+
let removed = 0;
61+
let bytes = 0;
62+
const walk = (dir) => {
63+
let entries;
64+
try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
65+
for (const e of entries) {
66+
const p = join(dir, e.name);
67+
if (e.isDirectory()) {
68+
if (dropDirName.test(e.name)) {
69+
const sz = sizeOf(p);
70+
rmSync(p, { recursive: true, force: true });
71+
removed++; bytes += sz;
72+
continue;
73+
}
74+
// Drop other-platform prebuilds (e.g. @libsql/linux-x64 on darwin)
75+
if (dir.endsWith('prebuilds') && !e.name.startsWith(platformTag)) {
76+
const sz = sizeOf(p);
77+
rmSync(p, { recursive: true, force: true });
78+
removed++; bytes += sz;
79+
continue;
80+
}
81+
walk(p);
82+
} else if (e.isFile()) {
83+
if (dropFileExt.test(e.name)) {
84+
try {
85+
const sz = statSync(p).size;
86+
unlinkSync(p);
87+
removed++; bytes += sz;
88+
} catch {}
89+
}
90+
}
91+
}
92+
};
93+
94+
function sizeOf(p) {
95+
try {
96+
let s = 0;
97+
for (const e of readdirSync(p, { withFileTypes: true })) {
98+
const c = join(p, e.name);
99+
if (e.isDirectory()) s += sizeOf(c);
100+
else { try { s += statSync(c).size; } catch {} }
101+
}
102+
return s;
103+
} catch { return 0; }
104+
}
105+
106+
walk(root);
107+
console.log(`✓ slimmed ${removed} entries (~${(bytes / 1024 / 1024).toFixed(1)} MB)`);
108+
}
109+
42110
if (!existsSync(join(APP, 'dist/objectstack.json'))) {
43111
sh('pnpm --filter @objectos/app build', { cwd: REPO });
44112
}
@@ -54,6 +122,10 @@ sh(
54122
{ cwd: join(RUNTIME, 'app') }
55123
);
56124

125+
// 3b. slim node_modules: drop source maps, docs, tests, .d.ts — these
126+
// are useless at runtime and bloat the installer by ~30%.
127+
slim(join(RUNTIME, 'app', 'node_modules'));
128+
57129
const cache = resolve(REPO, '.cache/node');
58130
mkdirSync(cache, { recursive: true });
59131
const ext = osName === 'win' ? 'zip' : osName === 'linux' ? 'tar.xz' : 'tar.gz';

0 commit comments

Comments
 (0)