Skip to content

Commit 17d034b

Browse files
committed
Squashed commit of the following:
commit d6c92ea Author: haritabh-z01 <haritabh.z01+github@gmail.com> Date: Thu Aug 21 23:27:44 2025 +0530 fix: unpacking amical/smart-whisper dep commit 8781981 Author: haritabh-z01 <haritabh.z01+github@gmail.com> Date: Thu Aug 21 17:41:02 2025 +0530 fix: unpacking of smart-whisper commit 81cec16 Author: haritabh-z01 <haritabh.z01+github@gmail.com> Date: Thu Aug 21 16:08:39 2025 +0530 chore: re-enable mac builds commit f13069c Author: haritabh-z01 <haritabh.z01+github@gmail.com> Date: Thu Aug 21 13:06:26 2025 +0530 feat: add smart-whisper package with updated build configuration commit a24e068 Author: haritabh-z01 <haritabh.z01+github@gmail.com> Date: Thu Aug 21 11:37:25 2025 +0530 chore: bump smart-whisper ver commit 98f84b6 Author: haritabh-z01 <haritabh.z01+github@gmail.com> Date: Wed Aug 20 08:59:55 2025 +0530 feat: release wf updates for win builds commit a85825d Author: haritabh-z01 <haritabh.z01+github@gmail.com> Date: Wed Aug 20 08:36:13 2025 +0530 feat: add windows support basics
1 parent 2d852a0 commit 17d034b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+10583
-3138
lines changed

.github/workflows/release.yml

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Release
22

33
on:
44
push:
5+
branches:
6+
- feat.windows.support
57
tags:
68
- 'v*'
79
workflow_dispatch:
@@ -12,18 +14,30 @@ on:
1214
type: string
1315

1416
jobs:
15-
build-macos:
16-
name: Build macOS (${{ matrix.arch }})
17-
runs-on: ${{ matrix.arch == 'x64' && 'macos-13' || 'macos-latest' }}
17+
build:
18+
name: Build ${{ matrix.os == 'macos' && 'macOS' || 'Windows' }} (${{ matrix.arch }})
19+
runs-on: ${{ matrix.runner }}
1820
strategy:
1921
matrix:
20-
arch: [arm64, x64]
22+
include:
23+
- os: macos
24+
arch: arm64
25+
runner: macos-latest
26+
- os: macos
27+
arch: x64
28+
runner: macos-13
29+
- os: windows
30+
arch: x64
31+
runner: windows-2025
2132

2233
steps:
2334
- name: Checkout code
2435
uses: actions/checkout@v4
36+
with:
37+
submodules: recursive
2538

2639
- name: Verify architecture
40+
if: matrix.os == 'macos'
2741
run: |
2842
CURRENT_ARCH=$(uname -m)
2943
echo "Current shell architecture: $CURRENT_ARCH"
@@ -47,12 +61,13 @@ jobs:
4761
- name: Setup pnpm
4862
uses: pnpm/action-setup@v4
4963
with:
50-
version: 10.13.1
64+
version: 10.15.0
5165

5266
- name: Setup Node.js
5367
uses: actions/setup-node@v4
5468
with:
55-
node-version: '24'
69+
# 24.2 to at least 24.6 (atm of writing this) has issues with symlink deref in nested directories
70+
node-version: '24.1.0'
5671
cache: 'pnpm'
5772

5873
- name: Log Node.js architecture and platform
@@ -70,15 +85,18 @@ jobs:
7085
run: pnpm download-node
7186

7287
- name: Import Developer ID cert
88+
if: matrix.os == 'macos'
7389
uses: apple-actions/import-codesign-certs@v3
7490
with:
7591
p12-file-base64: ${{ secrets.DEVELOPER_CERT_BASE64 }}
7692
p12-password: ${{ secrets.DEVELOPER_CERT_PASSPHRASE }}
7793

7894
- name: List signing identities (debug)
95+
if: matrix.os == 'macos'
7996
run: security find-identity -v -p codesigning
8097

81-
- name: Build artifacts
98+
- name: Build artifacts (macOS)
99+
if: matrix.os == 'macos'
82100
working-directory: apps/desktop
83101
env:
84102
SKIP_CODESIGNING: false
@@ -88,25 +106,43 @@ jobs:
88106
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
89107
CODESIGNING_IDENTITY: ${{ secrets.CODESIGNING_IDENTITY }}
90108
run: |
91-
echo "Building ${{ matrix.arch }} artifacts"
109+
echo "Building macOS ${{ matrix.arch }} artifacts"
92110
pnpm make:${{ matrix.arch }}
93111
112+
- name: Build artifacts (Windows)
113+
if: matrix.os == 'windows'
114+
working-directory: apps/desktop
115+
run: |
116+
echo "Building Windows x64 artifacts"
117+
pnpm make:windows
118+
94119
- name: Get version from package.json
95120
id: package_version
96121
working-directory: apps/desktop
122+
shell: bash
97123
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
98124

99-
- name: Upload artifacts
125+
- name: Upload artifacts (macOS)
126+
if: matrix.os == 'macos'
100127
uses: actions/upload-artifact@v4
101128
with:
102129
name: macos-${{ matrix.arch }}
103130
path: |
104131
apps/desktop/out/make/*-${{ matrix.arch }}.dmg
105132
apps/desktop/out/make/zip/darwin/${{ matrix.arch }}/*.zip
106133
134+
- name: Upload artifacts (Windows)
135+
if: matrix.os == 'windows'
136+
uses: actions/upload-artifact@v4
137+
with:
138+
name: windows-${{ matrix.arch }}
139+
path: |
140+
apps/desktop/out/make/squirrel.windows/${{ matrix.arch }}/*.exe
141+
apps/desktop/out/make/squirrel.windows/${{ matrix.arch }}/*.nupkg
142+
107143
release:
108144
name: Create Release
109-
needs: build-macos
145+
needs: build
110146
runs-on: ubuntu-latest
111147
permissions:
112148
contents: write
@@ -128,10 +164,10 @@ jobs:
128164
- name: List artifacts
129165
run: |
130166
echo "=== Full artifacts directory structure ==="
131-
find artifacts -type f -name "*.dmg" -o -name "*.zip" | sort
167+
find artifacts -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.nupkg" -o -name "RELEASES" \) | sort
132168
echo ""
133169
echo "=== Detailed file listing ==="
134-
find artifacts -type f \( -name "*.dmg" -o -name "*.zip" \) -exec ls -la {} \;
170+
find artifacts -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.nupkg" -o -name "RELEASES" \) -exec ls -la {} \;
135171
136172
- name: Create Release
137173
uses: softprops/action-gh-release@v2
@@ -152,17 +188,27 @@ jobs:
152188
- **Apple Silicon (M1/M2/M3)**: Download the DMG or ZIP file for arm64
153189
- **Intel**: Download the DMG or ZIP file for x64
154190
191+
#### Windows
192+
- **Windows (x64)**: Download the .exe installer for 64-bit Windows
193+
155194
### Installation
156195
157196
**macOS**:
158197
- **DMG**: Download and open the DMG file, then drag Amical to your Applications folder
159198
- **ZIP**: Download and extract the ZIP file, then drag Amical to your Applications folder
160199
161-
The ZIP files are primarily for automatic updates. We recommend using the DMG files for initial installation.
200+
**Windows**:
201+
- Download and run the .exe installer
202+
- Follow the installation wizard
203+
- The app will be installed to your user AppData folder and a shortcut will be created
204+
205+
The ZIP files are primarily for automatic updates. We recommend using the DMG files for initial installation on macOS.
162206
files: |
163207
artifacts/macos-arm64/*.dmg
164208
artifacts/macos-arm64/zip/darwin/arm64/*.zip
165209
artifacts/macos-x64/*.dmg
166210
artifacts/macos-x64/zip/darwin/x64/*.zip
211+
artifacts/windows-x64/*.exe
212+
artifacts/windows-x64/*.nupkg
167213
env:
168214
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "packages/smart-whisper/whisper.cpp"]
2+
path = packages/smart-whisper/whisper.cpp
3+
url = https://github.com/ggerganov/whisper.cpp.git

apps/desktop/assets/logo.ico

-11.5 KB
Binary file not shown.

apps/desktop/forge.config.ts

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
mkdirSync,
1818
cpSync,
1919
rmSync,
20+
lstatSync,
21+
readlinkSync,
2022
} from "node:fs";
2123
import { join, normalize } from "node:path";
2224
// Use flora-colossus for finding all dependencies of EXTERNAL_DEPENDENCIES
@@ -29,7 +31,6 @@ let nativeModuleDependenciesToPackage: string[] = [];
2931

3032
export const EXTERNAL_DEPENDENCIES = [
3133
"electron-squirrel-startup",
32-
"smart-whisper",
3334
"@libsql/client",
3435
"@libsql/darwin-arm64",
3536
"@libsql/darwin-x64",
@@ -39,13 +40,13 @@ export const EXTERNAL_DEPENDENCIES = [
3940
"libsql",
4041
"onnxruntime-node",
4142
"workerpool",
43+
"@amical/smart-whisper",
4244
// Add any other native modules you need here
4345
];
4446

4547
const config: ForgeConfig = {
4648
hooks: {
4749
prePackage: async (_forgeConfig, platform, arch) => {
48-
console.error("prePackage", { platform, arch });
4950
const projectRoot = normalize(__dirname);
5051
// In a monorepo, node_modules are typically at the root level
5152
const monorepoRoot = join(projectRoot, "../../"); // Go up to monorepo root
@@ -148,15 +149,56 @@ const config: ForgeConfig = {
148149

149150
// Copy the package
150151
console.log(`Copying ${dep}...`);
151-
cpSync(rootDepPath, localDepPath, { recursive: true });
152+
cpSync(rootDepPath, localDepPath, { recursive: true, dereference: true, force: true });
152153
console.log(`✓ Successfully copied ${dep}`);
153154
} catch (error) {
154155
console.error(`Failed to copy ${dep}:`, error);
155156
}
156157
}
157158

159+
// Second pass: Replace any symlinks with dereferenced copies
160+
console.log("Checking for symlinks in copied dependencies...");
161+
for (const dep of nativeModuleDependenciesToPackage) {
162+
const localDepPath = join(localNodeModules, dep);
163+
164+
try {
165+
if (existsSync(localDepPath)) {
166+
const stats = lstatSync(localDepPath);
167+
if (stats.isSymbolicLink()) {
168+
console.log(`Found symlink for ${dep}, replacing with dereferenced copy...`);
169+
170+
// Read where the symlink points to
171+
const symlinkTarget = readlinkSync(localDepPath);
172+
const absoluteTarget = join(localDepPath, "..", symlinkTarget);
173+
const sourcePath = normalize(absoluteTarget);
174+
175+
console.log(` Symlink points to: ${sourcePath}`);
176+
177+
// Remove the symlink
178+
rmSync(localDepPath, { recursive: true, force: true });
179+
180+
// Copy with dereference to get actual content
181+
cpSync(sourcePath, localDepPath, {
182+
recursive: true,
183+
force: true,
184+
dereference: true // Follow symlinks and copy actual content
185+
});
186+
187+
console.log(`✓ Successfully replaced symlink for ${dep} with actual content`);
188+
}
189+
}
190+
} catch (error) {
191+
console.error(`Failed to check/replace symlink for ${dep}:`, error);
192+
}
193+
}
194+
158195
// Prune onnxruntime-node to keep only the required binary
159-
console.log("Pruning onnxruntime-node binaries...");
196+
const targetPlatform = platform;
197+
const targetArch = arch;
198+
199+
console.log(
200+
`Pruning onnxruntime-node binaries for ${targetPlatform}/${targetArch}...`,
201+
);
160202
const onnxBinRoot = join(localNodeModules, "onnxruntime-node", "bin");
161203
if (existsSync(onnxBinRoot)) {
162204
const napiVersionDirs = readdirSync(onnxBinRoot);
@@ -169,18 +211,18 @@ const config: ForgeConfig = {
169211
const platformPath = join(napiVersionPath, platformDir);
170212
if (!statSync(platformPath).isDirectory()) continue;
171213

172-
// Delete other platform directories
173-
if (platformDir !== process.platform) {
214+
// Delete unused platforms except Linux (keep for compatibility)
215+
if (platformDir !== targetPlatform && platformDir !== "linux") {
174216
console.log(`- Deleting unused platform: ${platformPath}`);
175217
rmSync(platformPath, { recursive: true, force: true });
176-
} else {
218+
} else if (platformDir === targetPlatform) {
177219
// Now in the correct platform dir, prune architectures
178220
const archDirs = readdirSync(platformPath);
179221
for (const archDir of archDirs) {
180222
const archPath = join(platformPath, archDir);
181223
if (!statSync(archPath).isDirectory()) continue;
182224

183-
if (archDir !== process.arch) {
225+
if (archDir !== targetArch) {
184226
console.log(`- Deleting unused arch: ${archPath}`);
185227
rmSync(archPath, { recursive: true, force: true });
186228
}
@@ -196,6 +238,7 @@ const config: ForgeConfig = {
196238
}
197239
},
198240
packageAfterPrune: async (_forgeConfig, buildPath) => {
241+
console.error("PRE PACKAGE");
199242
try {
200243
function getItemsFromFolder(
201244
path: string,
@@ -264,14 +307,14 @@ const config: ForgeConfig = {
264307
packagerConfig: {
265308
asar: {
266309
unpack:
267-
"{*.node,*.dylib,*.so,*.dll,*.metal,**/whisper.cpp/**,**/.vite/build/whisper-worker-fork.js,**/node_modules/smart-whisper/**,**/node_modules/jest-worker/**}",
310+
"{*.node,*.dylib,*.so,*.dll,*.metal,**/node_modules/@amical/smart-whisper/**,**/whisper.cpp/**,**/.vite/build/whisper-worker-fork.js,**/node_modules/jest-worker/**,**/onnxruntime-node/bin/**}",
268311
},
269312
name: "Amical",
270313
executableName: "Amical",
271314
icon: "./assets/logo", // Path to your icon file
272315
appBundleId: "com.amical.desktop", // Proper bundle ID
273316
extraResource: [
274-
"../../packages/native-helpers/swift-helper/bin",
317+
`${process.platform === "win32" ? "../../packages/native-helpers/windows-helper/bin" : "../../packages/native-helpers/swift-helper/bin"}`,
275318
"./src/db/migrations",
276319
// Only include the platform-specific node binary
277320
`./node-binaries/${process.platform}-${process.arch}/node${
@@ -356,8 +399,18 @@ const config: ForgeConfig = {
356399
}
357400

358401
// Handle scoped packages: if dep is @scope/package, also keep @scope/ directory
402+
// But not for our workspace packages
359403
if (dep.includes("/") && dep.startsWith("@")) {
360404
const scopeDir = dep.split("/")[0]; // @libsql/client -> @libsql
405+
// for workspace packages only keep the actual package
406+
if (scopeDir === "@amical") {
407+
if (filePath.startsWith(`/node_modules/${dep}`) ||
408+
filePath === `/node_modules/${scopeDir}`) {
409+
KEEP_FILE.keep = true;
410+
KEEP_FILE.log = true;
411+
}
412+
continue;
413+
}
361414
if (
362415
filePath === `/node_modules/${scopeDir}/` ||
363416
filePath === `/node_modules/${scopeDir}` ||

0 commit comments

Comments
 (0)