Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f604ff1

Browse files
authored
Add Windows beta packaging and release assets (nexu-io#191)
1 parent dd283fa commit f604ff1

11 files changed

Lines changed: 1441 additions & 36 deletions

File tree

.github/workflows/release-beta.yml

Lines changed: 109 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,98 @@ jobs:
166166
name: open-design-beta-mac-release-assets
167167
path: ${{ runner.temp }}/release-assets
168168

169+
build_win:
170+
name: Build beta win x64
171+
needs: metadata
172+
runs-on: windows-latest
173+
env:
174+
GH_TOKEN: ${{ github.token }}
175+
steps:
176+
- name: Checkout
177+
uses: actions/[email protected]
178+
with:
179+
fetch-depth: 0
180+
181+
- name: Setup pnpm
182+
uses: pnpm/action-setup@v5
183+
with:
184+
version: 10.33.2
185+
186+
- name: Setup Node.js
187+
uses: actions/setup-node@v6
188+
with:
189+
node-version: 24
190+
191+
- name: Install dependencies
192+
run: pnpm install --frozen-lockfile
193+
194+
- name: Apply beta package version
195+
run: npm pkg set "version=${{ needs.metadata.outputs.beta_version }}" --prefix apps/packaged
196+
197+
- name: Build beta windows artifacts
198+
shell: pwsh
199+
run: >-
200+
pnpm exec tools-pack win build
201+
--dir "${{ runner.temp }}/tools-pack"
202+
--namespace release-beta-win
203+
--portable
204+
--to nsis
205+
--json
206+
207+
- name: Prepare windows beta assets
208+
shell: pwsh
209+
run: |
210+
$releaseDir = Join-Path $env:RUNNER_TEMP "release-assets"
211+
New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null
212+
213+
$sourceInstaller = Join-Path $env:RUNNER_TEMP "tools-pack/out/win/namespaces/release-beta-win/builder/Open Design-release-beta-win-setup.exe"
214+
$sourceBlockmap = Join-Path $env:RUNNER_TEMP "tools-pack/out/win/namespaces/release-beta-win/builder/Open Design-release-beta-win-setup.exe.blockmap"
215+
if (!(Test-Path $sourceInstaller)) {
216+
throw "expected installer not found at $sourceInstaller"
217+
}
218+
if (!(Test-Path $sourceBlockmap)) {
219+
throw "expected blockmap not found at $sourceBlockmap"
220+
}
221+
222+
$windowsAssetSuffix = ".unsigned"
223+
$versionedInstaller = "open-design-${{ needs.metadata.outputs.beta_version }}$windowsAssetSuffix-win-x64-setup.exe"
224+
$versionedBlockmap = "open-design-${{ needs.metadata.outputs.beta_version }}$windowsAssetSuffix-win-x64-setup.exe.blockmap"
225+
$checksumFile = "$versionedInstaller.sha256"
226+
Copy-Item $sourceInstaller (Join-Path $releaseDir $versionedInstaller)
227+
Copy-Item $sourceBlockmap (Join-Path $releaseDir $versionedBlockmap)
228+
229+
$installerPath = Join-Path $releaseDir $versionedInstaller
230+
$hash = (Get-FileHash -Path $installerPath -Algorithm SHA256).Hash.ToLowerInvariant()
231+
"$hash $versionedInstaller" | Set-Content -Path (Join-Path $releaseDir $checksumFile)
232+
$installerBytes = [System.IO.File]::ReadAllBytes($installerPath)
233+
$installerSha512 = [System.Convert]::ToBase64String([System.Security.Cryptography.SHA512]::Create().ComputeHash($installerBytes))
234+
$installerSize = (Get-Item $installerPath).Length
235+
$installerUrl = "https://github.com/$env:GITHUB_REPOSITORY/releases/download/${{ needs.metadata.outputs.version_tag }}/$versionedInstaller"
236+
$releaseDate = [DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
237+
@(
238+
'version: "${{ needs.metadata.outputs.beta_version }}"'
239+
'files:'
240+
" - url: `"$installerUrl`""
241+
" sha512: `"$installerSha512`""
242+
" size: $installerSize"
243+
"path: `"$installerUrl`""
244+
"sha512: `"$installerSha512`""
245+
"releaseDate: `"$releaseDate`""
246+
"releaseNotes: `"Open Design beta ${{ needs.metadata.outputs.beta_version }}$windowsAssetSuffix`""
247+
) | Set-Content -Path (Join-Path $releaseDir "latest.yml")
248+
249+
- name: Upload windows release bundle
250+
uses: actions/upload-artifact@v7
251+
with:
252+
name: open-design-beta-win-release-assets
253+
path: ${{ runner.temp }}/release-assets
254+
169255
publish:
170256
name: Publish beta release
171257
needs:
172258
- metadata
173259
- build_mac
260+
- build_win
174261
runs-on: ubuntu-latest
175262
env:
176263
GH_TOKEN: ${{ github.token }}
@@ -186,6 +273,12 @@ jobs:
186273
name: open-design-beta-mac-release-assets
187274
path: ${{ runner.temp }}/release-assets/mac
188275

276+
- name: Download windows release bundle
277+
uses: actions/download-artifact@v8
278+
with:
279+
name: open-design-beta-win-release-assets
280+
path: ${{ runner.temp }}/release-assets/win
281+
189282
- name: Move beta tags to current commit
190283
run: |
191284
set -euo pipefail
@@ -205,19 +298,20 @@ jobs:
205298
- channel: beta
206299
- version: ${{ needs.metadata.outputs.beta_version }}
207300
- base version: ${{ needs.metadata.outputs.base_version }}
208-
- signed: ${{ needs.metadata.outputs.signed }}
301+
- mac signed/notarized: ${{ needs.metadata.outputs.signed }}
302+
- windows signed: false
209303
- branch: ${{ needs.metadata.outputs.branch }}
210304
- commit: ${{ needs.metadata.outputs.commit }}
211305
212-
This beta release ships mac arm64 DMG, update ZIP, checksums, and a copy of latest-mac.yml.
306+
This beta release ships mac arm64 DMG/update ZIP, Windows x64 NSIS installer assets, checksums, and updater feed files.
213307
EOF
214308
cat > "$latest_notes_file" <<EOF
215309
## Summary
216310
- channel: beta
217311
- latest version: ${{ needs.metadata.outputs.beta_version }}
218312
- latest tag: ${{ needs.metadata.outputs.version_tag }}
219313
220-
This release is the mutable beta channel feed carrier. It should contain feed assets only.
314+
This release is the mutable beta channel feed carrier. It should contain feed assets only: latest-mac.yml and latest.yml.
221315
EOF
222316
{
223317
echo "version_notes_file=$version_notes_file"
@@ -228,6 +322,10 @@ jobs:
228322
run: |
229323
set -euo pipefail
230324
release_dir="$RUNNER_TEMP/release-assets/mac"
325+
all_release_dir="$RUNNER_TEMP/release-assets/all"
326+
mkdir -p "$all_release_dir"
327+
cp "$RUNNER_TEMP/release-assets/mac"/* "$all_release_dir/"
328+
cp "$RUNNER_TEMP/release-assets/win"/* "$all_release_dir/"
231329
if gh release view "${{ needs.metadata.outputs.version_tag }}" >/dev/null 2>&1; then
232330
gh release edit "${{ needs.metadata.outputs.version_tag }}" \
233331
--title "${{ needs.metadata.outputs.release_name }}" \
@@ -240,12 +338,13 @@ jobs:
240338
--notes-file "${{ steps.notes.outputs.version_notes_file }}" \
241339
--prerelease
242340
fi
243-
gh release upload "${{ needs.metadata.outputs.version_tag }}" "$release_dir"/* --clobber
341+
gh release upload "${{ needs.metadata.outputs.version_tag }}" "$all_release_dir"/* --clobber
244342
245343
- name: Create or update beta channel feed
246344
run: |
247345
set -euo pipefail
248346
latest_mac_path="$RUNNER_TEMP/release-assets/mac/latest-mac.yml"
347+
latest_win_path="$RUNNER_TEMP/release-assets/win/latest.yml"
249348
if gh release view "${{ needs.metadata.outputs.beta_tag }}" >/dev/null 2>&1; then
250349
while IFS= read -r asset_name; do
251350
if [ -n "$asset_name" ]; then
@@ -256,10 +355,10 @@ jobs:
256355
--title "Open Design Beta Latest" \
257356
--notes-file "${{ steps.notes.outputs.latest_notes_file }}" \
258357
--prerelease
259-
gh release upload "${{ needs.metadata.outputs.beta_tag }}" "$latest_mac_path" --clobber
358+
gh release upload "${{ needs.metadata.outputs.beta_tag }}" "$latest_mac_path" "$latest_win_path" --clobber
260359
else
261360
gh release create "${{ needs.metadata.outputs.beta_tag }}" \
262-
"$latest_mac_path" \
361+
"$latest_mac_path" "$latest_win_path" \
263362
--target "$GITHUB_SHA" \
264363
--title "Open Design Beta Latest" \
265364
--notes-file "${{ steps.notes.outputs.latest_notes_file }}" \
@@ -274,7 +373,9 @@ jobs:
274373
echo "- Version: ${{ needs.metadata.outputs.beta_version }}"
275374
echo "- Version tag: ${{ needs.metadata.outputs.version_tag }}"
276375
echo "- Channel feed tag: ${{ needs.metadata.outputs.beta_tag }}"
277-
echo "- Signed: ${{ needs.metadata.outputs.signed }}"
376+
echo "- mac signed/notarized: ${{ needs.metadata.outputs.signed }}"
377+
echo "- windows signed: false"
278378
echo "- mac assets: open-design-${{ needs.metadata.outputs.beta_version }}${{ needs.metadata.outputs.asset_version_suffix }}-mac-arm64.dmg, open-design-${{ needs.metadata.outputs.beta_version }}${{ needs.metadata.outputs.asset_version_suffix }}-mac-arm64.zip"
279-
echo "- Feed: latest-mac.yml"
379+
echo "- win assets: open-design-${{ needs.metadata.outputs.beta_version }}.unsigned-win-x64-setup.exe, open-design-${{ needs.metadata.outputs.beta_version }}.unsigned-win-x64-setup.exe.blockmap"
380+
echo "- Feeds: latest-mac.yml, latest.yml"
280381
} >> "$GITHUB_STEP_SUMMARY"

apps/daemon/src/server.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,18 @@ function resolveProcessResourcesPath() {
126126
// Infer the macOS app Resources directory from that bundled Node path.
127127
const resourcesMarker = `${path.sep}Contents${path.sep}Resources${path.sep}`;
128128
const markerIndex = process.execPath.indexOf(resourcesMarker);
129-
if (markerIndex === -1) return null;
129+
if (markerIndex !== -1) {
130+
return process.execPath.slice(0, markerIndex + resourcesMarker.length - 1);
131+
}
132+
133+
const normalizedExecPath = process.execPath.toLowerCase();
134+
const windowsResourceBinMarker = `${path.sep}resources${path.sep}open-design${path.sep}bin${path.sep}`.toLowerCase();
135+
const windowsMarkerIndex = normalizedExecPath.indexOf(windowsResourceBinMarker);
136+
if (windowsMarkerIndex !== -1) {
137+
return process.execPath.slice(0, windowsMarkerIndex + `${path.sep}resources`.length);
138+
}
130139

131-
return process.execPath.slice(0, markerIndex + resourcesMarker.length - 1);
140+
return null;
132141
}
133142

134143
export function resolveDaemonResourceRoot({

packages/sidecar/src/index.test.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { describe, expect, it } from "vitest";
2+
import { join, resolve } from "node:path";
23

34
import {
45
bootstrapSidecarRuntime,
@@ -68,18 +69,18 @@ describe("generic sidecar path boundary", () => {
6869
source: "tool",
6970
});
7071

71-
expect(sourceRoot).toBe("/repo/product/.fake-tmp/tool");
72+
expect(sourceRoot).toBe(resolve("/repo/product", ".fake-tmp", "tool"));
7273
expect(resolveNamespaceRoot({ base: sourceRoot, contract: fakeContract, namespace: "alpha" })).toBe(
73-
"/repo/product/.fake-tmp/tool/alpha",
74+
join(sourceRoot, "alpha"),
7475
);
7576
expect(
7677
resolveAppRuntimePath({
7778
app: "ui",
7879
contract: fakeContract,
7980
fileName: "cache",
80-
namespaceRoot: "/repo/product/.fake-tmp/tool/alpha",
81+
namespaceRoot: join(sourceRoot, "alpha"),
8182
}),
82-
).toBe("/repo/product/.fake-tmp/tool/alpha/ui/cache");
83+
).toBe(join(sourceRoot, "alpha", "ui", "cache"));
8384
});
8485

8586
it("resolves descriptor-specific IPC paths", () => {
@@ -95,34 +96,32 @@ describe("generic sidecar path boundary", () => {
9596
};
9697

9798
expect(resolveNamespace({ contract: fakeContract, env })).toBe("selected");
98-
expect(resolveSidecarBase({ contract: fakeContract, env, projectRoot: "/repo/product", source: "tool" })).toBe(
99-
"/runtime/base",
100-
);
99+
expect(resolveSidecarBase({ contract: fakeContract, env, projectRoot: "/repo/product", source: "tool" })).toBe(resolve("/runtime/base"));
101100
});
102101
});
103102

104103
describe("generic sidecar bootstrap", () => {
105104
it("creates and validates launch env from descriptor env names", () => {
106105
const stamp: FakeStamp = {
107106
app: "api",
108-
ipc: "/tmp/fake-product/ipc/alpha/api.sock",
107+
ipc: resolveAppIpcPath({ app: "api", contract: fakeContract, namespace: "alpha" }),
109108
mode: "dev",
110109
namespace: "alpha",
111110
source: "tool",
112111
};
113112

114113
expect(createSidecarLaunchEnv({ base: "/runtime/base", contract: fakeContract, extraEnv: {}, stamp })).toEqual({
115-
FAKE_BASE: "/runtime/base",
114+
FAKE_BASE: resolve("/runtime/base"),
116115
FAKE_IPC_PATH: stamp.ipc,
117116
FAKE_NAMESPACE: stamp.namespace,
118117
FAKE_SOURCE: stamp.source,
119118
});
120119

121120
expect(
122-
bootstrapSidecarRuntime(stamp, { FAKE_BASE: "/runtime/base" }, { app: "api", contract: fakeContract }),
121+
bootstrapSidecarRuntime(stamp, { FAKE_BASE: resolve("/runtime/base") }, { app: "api", contract: fakeContract }),
123122
).toEqual({
124123
app: "api",
125-
base: "/runtime/base",
124+
base: resolve("/runtime/base"),
126125
ipc: stamp.ipc,
127126
mode: "dev",
128127
namespace: "alpha",

tools/AGENTS.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ Follow the root `AGENTS.md` first. This file only records module-level boundarie
88
- `pnpm tools-dev` manages daemon -> web -> desktop.
99
- `pnpm tools-dev run web` runs foreground daemon + web for the Playwright webServer flow.
1010
- `pnpm tools-dev inspect desktop ...` inspects the desktop runtime through sidecar IPC.
11-
- `tools/pack` provides `@open-design/tools-pack` and the `tools-pack` bin. The active slice is mac-first packaged artifact build/install/start/stop/logs/uninstall/cleanup plus beta release artifact preparation.
11+
- `tools/pack` provides `@open-design/tools-pack` and the `tools-pack` bin. The active slice is packaged artifact build/install/start/stop/logs/uninstall/cleanup/list/reset plus beta release artifact preparation for mac and Windows lanes.
1212

1313
## Packaging scope
1414

15-
- Keep `tools-pack` focused on mac packaging/runtime control and release artifact preparation. Runtime updater integration and Windows packaging remain later phases.
15+
- Keep `tools-pack` focused on packaging/runtime control and release artifact preparation. Runtime updater product integration remains a later phase.
1616
- Pack-specific Electron builder resources belong under `tools/pack/resources/`; do not reference app/docs/download assets directly from pack logic.
1717
- Namespace controls packaged data/log/runtime/cache paths. Ports are transient transport details and must not participate in path decisions.
1818
- The package/build boundary of root `pnpm build` is intentionally unchanged in this round and should be handled by the future `tools-pack` task.
@@ -36,4 +36,8 @@ pnpm tools-dev check
3636
pnpm tools-pack mac build --to all
3737
pnpm tools-pack mac install
3838
pnpm tools-pack mac cleanup
39+
pnpm tools-pack win build --to nsis
40+
pnpm tools-pack win install
41+
pnpm tools-pack win inspect --expr "document.title"
42+
pnpm tools-pack win cleanup
3943
```

tools/pack/AGENTS.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ Follow the root `AGENTS.md` and `tools/AGENTS.md` first. This tool owns the repo
55
## Owns
66

77
- Local packaging orchestration for packaged Open Design artifacts.
8-
- mac-first build/install/start/stop/logs/uninstall/cleanup smoke commands.
8+
- mac build/install/start/stop/logs/uninstall/cleanup smoke commands.
9+
- Windows NSIS build/install/start/stop/logs/uninstall/cleanup/list/reset smoke commands.
10+
- Windows registry observation/cleanup must go through `reg.exe` and stay scoped to entries matching the namespace install/uninstaller paths.
11+
- Windows lifecycle logs must expose NSIS automation logs/markers/timings in addition to app runtime logs.
912
- Consuming sidecar/process/path primitives from `@open-design/sidecar-proto`, `@open-design/sidecar`, and `@open-design/platform`.
1013

1114
## Does not own
@@ -19,7 +22,7 @@ Follow the root `AGENTS.md` and `tools/AGENTS.md` first. This tool owns the repo
1922

2023
- Do not hand-build `--od-stamp-*` args; use `createProcessStampArgs` with `OPEN_DESIGN_SIDECAR_CONTRACT`.
2124
- Do not use port numbers in data/log/runtime/cache path decisions. Namespace decides paths; ports are only transient transports.
22-
- Release artifacts keep canonical `Open Design.app`; local tools-pack installs may use `Open Design.<namespace>.app` only as an install-path/app-bundle naming convention for developer multi-instance validation.
25+
- Release artifacts keep canonical app identity (`Open Design.app` on mac, `Open Design.exe` inside the Windows installer); local tools-pack installs may use namespace-scoped install paths only as a developer multi-instance validation convention.
2326
- Do not let namespace-named `.app` installs change data/log/runtime/cache path conventions.
2427
- Use `--portable` for public/release artifacts so packaged config does not bake local tools-pack runtime roots from the build machine.
2528
- Pack resource files used by electron-builder belong under `tools/pack/resources/`; do not point pack logic at Downloads, web public assets, docs assets, or other app-owned resource paths.

tools/pack/resources/win/icon.ico

62.3 KB
Binary file not shown.

tools/pack/src/config.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,22 @@ const ENTRY_DIR_NAME = path.basename(__dirname);
1414

1515
export const WORKSPACE_ROOT = resolve(__dirname, ENTRY_DIR_NAME === "dist" ? "../../.." : "../../..");
1616

17-
export type ToolPackPlatform = "mac";
18-
export type ToolPackBuildOutput = "all" | "app" | "dmg" | "zip";
17+
export type ToolPackPlatform = "mac" | "win";
18+
export type ToolPackBuildOutput = "all" | "app" | "dir" | "dmg" | "nsis" | "zip";
1919

2020
export type ToolPackCliOptions = {
2121
dir?: string;
22+
expr?: string;
2223
json?: boolean;
2324
namespace?: string;
25+
path?: string;
2426
portable?: boolean;
27+
removeData?: boolean;
28+
removeLogs?: boolean;
29+
removeProductUserData?: boolean;
30+
removeSidecars?: boolean;
2531
signed?: boolean;
32+
silent?: boolean;
2633
to?: string;
2734
};
2835

@@ -47,16 +54,22 @@ export type ToolPackConfig = {
4754
namespace: string;
4855
platform: ToolPackPlatform;
4956
portable: boolean;
57+
removeData: boolean;
58+
removeLogs: boolean;
59+
removeProductUserData: boolean;
60+
removeSidecars: boolean;
5061
roots: ToolPackRoots;
62+
silent: boolean;
5163
signed: boolean;
5264
to: ToolPackBuildOutput;
5365
workspaceRoot: string;
5466
};
5567

56-
function resolveToolPackBuildOutput(value: string | undefined): ToolPackBuildOutput {
57-
if (value == null || value.length === 0) return "all";
58-
if (value === "all" || value === "app" || value === "dmg" || value === "zip") return value;
59-
throw new Error(`unsupported mac --to target: ${value}`);
68+
function resolveToolPackBuildOutput(platform: ToolPackPlatform, value: string | undefined): ToolPackBuildOutput {
69+
if (value == null || value.length === 0) return platform === "win" ? "nsis" : "all";
70+
if (platform === "mac" && (value === "all" || value === "app" || value === "dmg" || value === "zip")) return value;
71+
if (platform === "win" && (value === "all" || value === "dir" || value === "nsis")) return value;
72+
throw new Error(`unsupported ${platform} --to target: ${value}`);
6073
}
6174

6275
function resolveElectronVersion(workspaceRoot: string): string {
@@ -117,8 +130,13 @@ export function resolveToolPackConfig(
117130
},
118131
toolPackRoot,
119132
},
133+
removeData: options.removeData === true,
134+
removeLogs: options.removeLogs === true,
135+
removeProductUserData: options.removeProductUserData === true,
136+
removeSidecars: options.removeSidecars === true,
137+
silent: options.silent !== false,
120138
signed: options.signed === true,
121-
to: resolveToolPackBuildOutput(options.to),
139+
to: resolveToolPackBuildOutput(platform, options.to),
122140
workspaceRoot: WORKSPACE_ROOT,
123141
};
124142
}

0 commit comments

Comments
 (0)