@sdods/cli 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/commands/analyze.d.ts +2 -0
- package/dist/commands/analyze.js +12 -32
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +29 -2
- package/package.json +8 -8
- package/templates/.claude/skills/sdods-release-channels/SKILL.md +185 -0
- package/templates/.claude/skills/code-signing/SKILL.md +0 -238
- package/templates/.claude/skills/sdods-desktop-release/SKILL.md +0 -170
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: sdods-desktop-release
|
|
3
|
-
description: Build, test, release and publish the SDODS desktop app (apps/desktop) for macOS, Windows and Linux, and update the download page on sdods.com. Use when asked to build the desktop app, cut a desktop release, tag desktop-v*, publish installers, refresh the download page, or debug a packaged build that will not start.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Releasing the SDODS desktop app
|
|
7
|
-
|
|
8
|
-
The desktop app is an Electron **supervisor**: it owns a private SDODS workspace, installs
|
|
9
|
-
`@sdods/cli` from npm into it, and runs `sdods serve` as a child of a **bundled Node runtime**. It
|
|
10
|
-
does not reimplement SDODS and it does not host the server in-process.
|
|
11
|
-
|
|
12
|
-
Read this before changing anything in `apps/desktop` — most of it is here because something failed
|
|
13
|
-
in a way that produced no error message.
|
|
14
|
-
|
|
15
|
-
## The loop
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# 1. Prove the runtime contract (no Electron involved). Catches npm/init/native-module problems.
|
|
19
|
-
bun run --cwd apps/desktop probe
|
|
20
|
-
|
|
21
|
-
# 2. Develop
|
|
22
|
-
bun run desktop:dev
|
|
23
|
-
|
|
24
|
-
# 3. Package for this machine
|
|
25
|
-
bun run --cwd apps/desktop dist:mac # dist:win · dist:linux
|
|
26
|
-
|
|
27
|
-
# 4. Release
|
|
28
|
-
git tag desktop-v0.1.0 && git push origin desktop-v0.1.0 # triggers .github/workflows/desktop.yml
|
|
29
|
-
# ... workflow drafts a release in the PUBLIC releases repo; publish it by hand ...
|
|
30
|
-
bun run desktop:sync-release desktop-v0.1.0 # writes apps/www/lib/desktop-release.ts
|
|
31
|
-
git commit -am 'chore(www): desktop 0.1.0 downloads' && merge to main # www workflow deploys
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**Order matters.** Sync the manifest only after the release is *published*, not while it is a
|
|
35
|
-
draft — the download page links straight at the asset URLs, and draft assets are not downloadable.
|
|
36
|
-
The sync script refuses to run against a draft for exactly this reason.
|
|
37
|
-
|
|
38
|
-
## Where the binaries live, and why not in this repo
|
|
39
|
-
|
|
40
|
-
`siri1410/SDODS` is **private**, and **release assets on a private repo are private too** — a
|
|
41
|
-
GitHub download link would 404 for every visitor. So installers are published to a separate
|
|
42
|
-
**public** repository that holds nothing but releases:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
gh repo create siri1410/sdods-releases --public -d 'SDODS desktop installers'
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Then, on the source repo: a `DESKTOP_RELEASE_TOKEN` secret (a fine-grained PAT with
|
|
49
|
-
`Contents: read+write` on the releases repo **only** — `GITHUB_TOKEN` cannot write across
|
|
50
|
-
repositories), and optionally a `DESKTOP_RELEASE_REPO` variable to point somewhere else.
|
|
51
|
-
|
|
52
|
-
Source stays closed; only the built installers are public. This also keeps `electron-updater`
|
|
53
|
-
straightforward later, since it reads GitHub releases natively.
|
|
54
|
-
|
|
55
|
-
**Beware:** `git ls-remote https://github.com/siri1410/SDODS.git` **succeeds** on a machine with
|
|
56
|
-
`gh auth` configured, because its credential helper is global. That is not an anonymous probe and
|
|
57
|
-
it has produced the wrong conclusion here twice. Check visibility with
|
|
58
|
-
`gh api repos/<slug> --jq .private`.
|
|
59
|
-
|
|
60
|
-
## Naming and versioning
|
|
61
|
-
|
|
62
|
-
- **SemVer** on `apps/desktop/package.json`; the tag is `desktop-v<version>`, kept separate from
|
|
63
|
-
the `@sdods/*` npm versions because the app ships on its own cadence.
|
|
64
|
-
- **Every artifact says what it is**: `<product>-<version>-<platform>-<arch>.<ext>` —
|
|
65
|
-
`SDODS-0.1.0-mac-arm64.dmg`, `SDODS-Setup-0.1.0-win-x64.exe`,
|
|
66
|
-
`SDODS-0.1.0-linux-x64.AppImage`. electron-builder's defaults omit the platform on macOS, so
|
|
67
|
-
`SDODS-0.1.0-arm64.dmg` could equally be a Linux build in a listing of six files.
|
|
68
|
-
- **`SHA256SUMS.txt`** ships with every release. It matters more than usual while builds are
|
|
69
|
-
unsigned: it is the only way a user can verify what they downloaded.
|
|
70
|
-
`sha256sum -c SHA256SUMS.txt --ignore-missing`
|
|
71
|
-
|
|
72
|
-
## Verifying a build actually works
|
|
73
|
-
|
|
74
|
-
A packaged build that starts is not the same as one that works. The real test is a run started
|
|
75
|
-
from the app's own Runs page — that exercises `process.execPath`, the workspace layout and the
|
|
76
|
-
browser cache at once.
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
# Install like a user, then launch with NOTHING on PATH. This is the zero-prerequisite promise.
|
|
80
|
-
env -i HOME="$HOME" USER="$USER" TMPDIR="$TMPDIR" PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
|
|
81
|
-
SDODS_DESKTOP_WORKSPACE=/tmp/sdods-test \
|
|
82
|
-
/Applications/SDODS.app/Contents/MacOS/SDODS
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Then: Runs → Start run → layer `ui`, browser `chromium`, tags `@smoke` → Run. It should pass 4/0/0.
|
|
86
|
-
|
|
87
|
-
Useful env vars:
|
|
88
|
-
|
|
89
|
-
| Variable | Purpose |
|
|
90
|
-
|---|---|
|
|
91
|
-
| `SDODS_DESKTOP_WORKSPACE` | Override the `~/SDODS` default. Always set this when testing, or you litter the real home directory. |
|
|
92
|
-
| `SDODS_DESKTOP_NODE` | Point at a specific `node` binary instead of the staged/system one. |
|
|
93
|
-
|
|
94
|
-
Logs: `~/Library/Application Support/SDODS/logs/desktop.log` (menu → Open Logs Folder). App state
|
|
95
|
-
lives beside it; `rm -rf` that directory for a clean first-run test.
|
|
96
|
-
|
|
97
|
-
## Traps — each one cost a debugging session
|
|
98
|
-
|
|
99
|
-
**Never spawn `process.execPath`.** Under Electron that is the Electron binary, so spawning it
|
|
100
|
-
launches a second copy of the app, which hits the single-instance lock and dies **silently**. Use
|
|
101
|
-
`nodeBin()` from `src/main/runtime.ts`. Same reason the server is a child process rather than
|
|
102
|
-
in-process: `packages/server/src/services/cli.ts` builds every child argv from `process.execPath`.
|
|
103
|
-
|
|
104
|
-
**Never spawn `npm` by bare name.** Windows has `npm.cmd`, not `npm`, and a GUI app launched from
|
|
105
|
-
the Dock inherits a minimal PATH. Use `npmCli()`, which resolves npm's own entry point.
|
|
106
|
-
|
|
107
|
-
**A missing `extraResources` source is only a warning.** It once produced an x64 `.dmg` with no
|
|
108
|
-
Node runtime and a zero exit code. `scripts/before-pack.mjs` stages the runtime per arch and fails
|
|
109
|
-
hard; `desktop.yml` re-checks every packaged app. Do not remove either.
|
|
110
|
-
|
|
111
|
-
**Signals do not fire in the packaged app.** A SIGTERM runs none of `before-quit`, `will-quit`,
|
|
112
|
-
`exit`, or `process.on('SIGTERM')` — Electron terminates natively — so the detached server child
|
|
113
|
-
outlives the app. The guarantee is the pidfile: the server's pid is recorded and the next launch
|
|
114
|
-
reaps it. Test it with `kill -9` on the app, not `kill`.
|
|
115
|
-
|
|
116
|
-
**Browsers are needed for every layer, not just UI.** SDODS merges one BDD fixture set across
|
|
117
|
-
layers, so an api-layer run against an empty `PLAYWRIGHT_BROWSERS_PATH` fails with
|
|
118
|
-
`browserType.launch: Executable doesn't exist`. Chromium is fetched after the dashboard loads.
|
|
119
|
-
|
|
120
|
-
**`sdods init` needs `--force --no-install --no-browsers`.** It refuses a non-empty directory,
|
|
121
|
-
its `--pm` accepts only `bun|pnpm` (neither is on a user machine), and it would pull browsers. It
|
|
122
|
-
overwrites `package.json` on purpose — its manifest declares `@playwright/test` and
|
|
123
|
-
`playwright-bdd`, which `sdods run` needs — so npm install runs again afterwards.
|
|
124
|
-
|
|
125
|
-
**`@sdods/server@0.2.1` hardcodes `cliBin: resolve(rootDir, 'packages/cli/src/bin.ts')`**, a
|
|
126
|
-
monorepo-only path, so UI-triggered runs die with ERR_MODULE_NOT_FOUND. `bootstrap.ts` writes a
|
|
127
|
-
bridge at that path, but only while the installed server still contains that string. **Publishing a
|
|
128
|
-
server newer than 0.2.1 removes the need for it** and fixes `sdods serve` for every npm user.
|
|
129
|
-
|
|
130
|
-
**Do not build the macOS `universal` target.** It lipo-merges two packs that each want a different
|
|
131
|
-
`node` binary at one path. `before-pack.mjs` rejects it. Ship separate arm64 and x64 artifacts.
|
|
132
|
-
|
|
133
|
-
**`apps/desktop` must declare no production dependencies.** electron-builder's dependency collector
|
|
134
|
-
has no handling for bun's `node_modules/.bun` symlink layout. Everything is bundled by rollup and
|
|
135
|
-
`files` excludes `node_modules` outright. Adding a runtime dependency will break packaging.
|
|
136
|
-
|
|
137
|
-
## Browser architecture detection on the download page
|
|
138
|
-
|
|
139
|
-
An Apple silicon Mac reports `Intel Mac OS X` in its user agent. Parsing the UA alone recommends
|
|
140
|
-
the Intel build to nearly every modern Mac. `detectArch()` uses
|
|
141
|
-
`navigator.userAgentData.getHighEntropyValues(['architecture'])`, which is truthful on Chromium;
|
|
142
|
-
Safari and Firefox return nothing, so macOS defaults to Apple silicon deliberately. Every other
|
|
143
|
-
build is listed underneath, and the full list is server-rendered so no-JS visitors lose nothing.
|
|
144
|
-
|
|
145
|
-
## Signing
|
|
146
|
-
|
|
147
|
-
Builds are unsigned today, so macOS shows "damaged / unidentified developer" and Windows shows
|
|
148
|
-
SmartScreen. The download page prints the per-OS workaround automatically while
|
|
149
|
-
`DESKTOP_RELEASE.signed` is false.
|
|
150
|
-
|
|
151
|
-
Everything is wired behind CI secrets already — supply them and signing turns on with no code
|
|
152
|
-
change: `CSC_LINK`, `CSC_KEY_PASSWORD`, `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`
|
|
153
|
-
for macOS; `WIN_CSC_LINK`, `WIN_CSC_KEY_PASSWORD` for Windows. Obtaining the certificates requires
|
|
154
|
-
a person: Apple Developer Program ($99/yr), and for Windows **Azure Trusted Signing** (~$10/month)
|
|
155
|
-
rather than a traditional OV certificate, which has required an FIPS hardware token since June 2023
|
|
156
|
-
and does not fit CI. After signing lands, pass `--signed` to `desktop:sync-release`.
|
|
157
|
-
|
|
158
|
-
## Files
|
|
159
|
-
|
|
160
|
-
| Path | What it is |
|
|
161
|
-
|---|---|
|
|
162
|
-
| `apps/desktop/src/main/index.ts` | Lifecycle: bootstrap → serve → authenticate → load. Pidfile and orphan reaping. |
|
|
163
|
-
| `apps/desktop/src/main/runtime.ts` | Bundled Node resolution and the child environment. |
|
|
164
|
-
| `apps/desktop/src/main/bootstrap.ts` | The five-step first-run install, and the 0.2.1 CLI bridge. |
|
|
165
|
-
| `apps/desktop/src/main/server.ts` | Port choice, `sdods serve` child, health poll, setup-token capture. |
|
|
166
|
-
| `apps/desktop/src/main/auth.ts` | Admin creation, safeStorage vault, cookie injection. |
|
|
167
|
-
| `apps/desktop/scripts/probe.ts` | The runtime contract, provable without Electron. |
|
|
168
|
-
| `apps/desktop/scripts/fetch-node-runtime.ts` | Downloads + SHASUMS-verifies + prunes Node. |
|
|
169
|
-
| `scripts/sync-desktop-release.ts` | GitHub release → `apps/www/lib/desktop-release.ts`. |
|
|
170
|
-
| `.github/workflows/desktop.yml` | Tag-gated matrix build, artifact verification, draft release. |
|