Compare commits

..

8 Commits

Author SHA1 Message Date
bincxz
72635eeaeb fix(ci): upgrade Node.js from 20 to 22 for @electron/rebuild compat
Some checks failed
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
@electron/rebuild@4.0.3 requires Node >= 22.12.0
2026-03-06 02:34:24 +08:00
bincxz
ec17abb507 Merge pull request: feat: enable macOS code signing and notarization
- Enable hardenedRuntime and notarize in electron-builder config
- Remove FixQuarantine.app workaround and DMG background image
- Pass signing and notarization secrets in CI build step
2026-03-06 02:07:10 +08:00
bincxz
fe7f760a47 chore: remove DMG background image 2026-03-06 02:06:50 +08:00
bincxz
ab70a406c9 feat: enable macOS code signing and notarization
- Enable hardenedRuntime and notarize in electron-builder config
- Remove FixQuarantine.app workaround from DMG (no longer needed
  with proper code signing)
- Pass signing and notarization secrets in CI build step
- Shrink DMG window to fit the simpler two-icon layout
2026-03-06 01:48:49 +08:00
bincxz
7e73da5557 Merge pull request #277 from binaricat/fix/issue-264-linux-x64-revert-container
fix(ci): revert Linux x64 build to ubuntu-latest without container

Closes #264
2026-03-06 01:45:47 +08:00
bincxz
97474acb89 fix(ci): revert Linux x64 build to ubuntu-latest without container
The debian:bullseye container introduced in v1.0.39 broke native module
packaging — node-pty's .node binary was missing from app.asar.unpacked,
causing 'No such file or directory' on ArchLinux and other x64 distros.

Revert to the v1.0.38 approach: build x64 directly on ubuntu-latest
with setup-node. ARM64 keeps the Debian container for GLIBC compat.

Closes #264
2026-03-06 01:44:08 +08:00
陈大猫
f59c83be2a fix: await provider token decryption before creating sync adapters (#276)
* fix: await provider token decryption before creating sync adapters

On cold start, initProviderDecryption() runs async in the constructor
but getConnectedAdapter() could be called before it finished, causing
adapter creation with still-encrypted tokens to fail silently.

Store the decryption promise and await it in getConnectedAdapter() so
tokens are guaranteed to be decrypted before use.

* fix: auto-recover sync providers stuck in error status

When syncAllProviders runs, providers with status 'error' that still
have tokens/config are now reset to 'connected' and their cached
adapter is invalidated, allowing a fresh retry with current (decrypted)
tokens. This prevents the permanent 'not configured' state that
previously required opening Settings to clear.
2026-03-06 01:38:18 +08:00
陈大猫
cba1803230 fix: install Linux icons in standard hicolor sizes (#274)\n\nGenerate 16x16 through 512x512 icon PNGs in build/icons/ so\nelectron-builder installs them to the correct hicolor directories\ninstead of only 1024x1024.\n\nUpdate .gitignore to track build/icons/ while keeping other\nbuild artifacts ignored.\n\nCloses #274 (#275) 2026-03-06 01:10:22 +08:00
11 changed files with 44 additions and 35 deletions

View File

@@ -37,7 +37,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
cache: npm
- name: Install deps
@@ -58,8 +58,13 @@ jobs:
- name: Build package
env:
CSC_IDENTITY_AUTO_DISCOVERY: ${{ matrix.name == 'macos' && 'false' || '' }}
ELECTRON_BUILDER_PUBLISH: "never"
# macOS code signing & notarization (ignored on other platforms)
CSC_LINK: ${{ secrets.MAC_CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: npm run ${{ matrix.pack_script }}
- name: Upload artifacts
@@ -76,30 +81,28 @@ jobs:
release/*.tar.gz
if-no-files-found: ignore
# Dedicated job for Linux x64 — builds inside Debian Bullseye (GLIBC 2.31)
# to ensure native modules (node-pty, ssh2) compile correctly and to
# maintain GLIBC compatibility with older distros.
# Linux x64 — builds directly on ubuntu-latest (no container).
# v1.0.39 used a debian:bullseye container which broke native module
# packaging (node-pty .node file missing from asar.unpacked). Reverted
# to the v1.0.38 approach. See #264.
build-linux-x64:
name: build-linux-x64
runs-on: ubuntu-latest
container:
image: debian:bullseye
env:
VITE_SYNC_GITHUB_CLIENT_ID: ${{ secrets.VITE_SYNC_GITHUB_CLIENT_ID }}
VITE_SYNC_GOOGLE_CLIENT_ID: ${{ secrets.VITE_SYNC_GOOGLE_CLIENT_ID }}
VITE_SYNC_GOOGLE_CLIENT_SECRET: ${{ secrets.VITE_SYNC_GOOGLE_CLIENT_SECRET }}
VITE_SYNC_ONEDRIVE_CLIENT_ID: ${{ secrets.VITE_SYNC_ONEDRIVE_CLIENT_ID }}
steps:
- name: Install build dependencies
run: |
apt-get update
apt-get install -y curl build-essential python3 git libfuse2 file rpm
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install deps
run: npm ci
@@ -147,7 +150,7 @@ jobs:
run: |
apt-get update
apt-get install -y curl build-essential python3 git libfuse2 file rpm
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs
- name: Checkout

3
.gitignore vendored
View File

@@ -17,7 +17,8 @@ dist-ssr
*.tsbuildinfo
coverage
/.vite
/build
/build/*
!/build/icons
/electron/native/**/build
/release
/out

BIN
build/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
build/icons/16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

BIN
build/icons/256x256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
build/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
build/icons/48x48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
build/icons/512x512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
build/icons/64x64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -1,6 +1,3 @@
/* global __dirname */
const path = require('path');
/**
* @type {import('electron-builder').Configuration}
*/
@@ -37,8 +34,9 @@ module.exports = {
}
],
category: 'public.app-category.developer-tools',
hardenedRuntime: false,
hardenedRuntime: true,
gatekeeperAssess: false,
notarize: true,
entitlements: 'electron/entitlements.mac.plist',
entitlementsInherit: 'electron/entitlements.mac.plist',
extendInfo: {
@@ -49,24 +47,15 @@ module.exports = {
},
dmg: {
title: '${productName}',
background: 'public/dmg-background.jpg',
iconSize: 100,
iconTextSize: 12,
window: {
width: 672,
height: 500
width: 540,
height: 380
},
contents: [
{ x: 150, y: 158 },
{ x: 550, y: 158, type: 'link', path: '/Applications' },
{
x: 350,
y: 330,
type: 'file',
// Use absolute path resolved at build time
path: path.resolve(__dirname, 'scripts/FixQuarantine.app'),
name: '已损坏修复.app'
}
{ x: 140, y: 158 },
{ x: 400, y: 158, type: 'link', path: '/Applications' }
]
},
win: {

View File

@@ -83,6 +83,9 @@ export class CloudSyncManager {
private autoSyncTimer: ReturnType<typeof setInterval> | null = null;
private masterPassword: string | null = null; // In memory only!
private hasStorageListener = false;
// Promise that resolves once startup provider secret decryption finishes.
// Awaited by getConnectedAdapter() to prevent using still-encrypted tokens.
private decryptionReady: Promise<void>;
// Per-provider sequence counters for async decrypt callbacks (startup,
// cross-window storage events). Bumped by any state mutation so stale
// decrypt results are discarded.
@@ -101,7 +104,7 @@ export class CloudSyncManager {
this.stateSnapshot = { ...this.state };
this.setupCrossWindowSync();
// Decrypt provider secrets asynchronously after initial load
this.initProviderDecryption();
this.decryptionReady = this.initProviderDecryption();
}
// ==========================================================================
@@ -399,6 +402,8 @@ export class CloudSyncManager {
};
private async getConnectedAdapter(provider: CloudProvider): Promise<CloudAdapter> {
// Ensure startup decryption has finished before reading tokens
await this.decryptionReady;
const connection = this.state.providers[provider];
const tokens = connection?.tokens;
const config = connection?.config;
@@ -1210,7 +1215,18 @@ export class CloudSyncManager {
}
const connectedProviders = Object.entries(this.state.providers)
.filter(([_, conn]) => conn.status === 'connected')
.filter(([p, conn]) => {
if (conn.status === 'connected') return true;
// Auto-recover: retry providers stuck in 'error' if tokens/config still exist
if (conn.status === 'error' && (conn.tokens || conn.config)) {
this.state.providers[p as CloudProvider].status = 'connected';
this.state.providers[p as CloudProvider].error = undefined;
// Clear cached adapter so a fresh one is created with current (decrypted) tokens
this.adapters.delete(p as CloudProvider);
return true;
}
return false;
})
.map(([p]) => p as CloudProvider);
if (connectedProviders.length === 0) {