feat(mac): add ARM64 and x64 distribution scripts and update README
- Introduced new distribution scripts for macOS targeting ARM64 and x64 architectures. - Updated README to clarify download instructions for macOS Apple Silicon and Intel users. - Enhanced CI workflow to support building for both architectures, improving release process.
This commit is contained in:
parent
15e80406fa
commit
8729039698
4 changed files with 86 additions and 12 deletions
20
.github/workflows/release.yml
vendored
20
.github/workflows/release.yml
vendored
|
|
@ -49,7 +49,17 @@ jobs:
|
|||
|
||||
release-mac:
|
||||
needs: build
|
||||
runs-on: macos-14
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: arm64
|
||||
runner: macos-14
|
||||
dist_command: pnpm dist:mac:arm64
|
||||
- arch: x64
|
||||
runner: macos-13
|
||||
dist_command: pnpm dist:mac:x64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
@ -83,16 +93,16 @@ jobs:
|
|||
VERSION="${GITHUB_REF#refs/tags/v}"
|
||||
pnpm pkg set version="$VERSION"
|
||||
|
||||
- name: Build app (macOS)
|
||||
- name: Build app (macOS ${{ matrix.arch }})
|
||||
run: pnpm build
|
||||
|
||||
- name: Verify packaged inputs (macOS)
|
||||
- name: Verify packaged inputs (macOS ${{ matrix.arch }})
|
||||
run: |
|
||||
test -f dist-electron/main/index.cjs
|
||||
test -f dist-electron/preload/index.js
|
||||
test -f out/renderer/index.html
|
||||
|
||||
- name: Package & Release (macOS)
|
||||
- name: Package & Release (macOS ${{ matrix.arch }})
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
|
|
@ -100,7 +110,7 @@ jobs:
|
|||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
run: pnpm dist:mac
|
||||
run: ${{ matrix.dist_command }}
|
||||
|
||||
release-win:
|
||||
needs: build
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@
|
|||
|
||||
| Platform | Download | Notes |
|
||||
|----------|----------|-------|
|
||||
| **macOS** (Apple Silicon) | [`.dmg`](https://github.com/matt1398/claude-devtools/releases/latest) | Drag to Applications. On first launch: right-click → Open |
|
||||
| **macOS** (Apple Silicon) | [`.dmg`](https://github.com/matt1398/claude-devtools/releases/latest) | Download the `arm64` asset. Drag to Applications. On first launch: right-click → Open |
|
||||
| **macOS** (Intel) | [`.dmg`](https://github.com/matt1398/claude-devtools/releases/latest) | Download the `x64` asset. Drag to Applications. On first launch: right-click → Open |
|
||||
| **Windows** | [`.exe`](https://github.com/matt1398/claude-devtools/releases/latest) | Standard installer. May trigger SmartScreen — click "More info" → "Run anyway" |
|
||||
|
||||
The app reads session logs from `~/.claude/` — the data is already on your machine. No setup, no API keys, no login.
|
||||
|
|
@ -201,7 +202,8 @@ The app auto-discovers your Claude Code projects from `~/.claude/`.
|
|||
#### Build for Distribution
|
||||
|
||||
```bash
|
||||
pnpm dist:mac # macOS (.dmg)
|
||||
pnpm dist:mac:arm64 # macOS Apple Silicon (.dmg)
|
||||
pnpm dist:mac:x64 # macOS Intel (.dmg)
|
||||
pnpm dist:win # Windows (.exe)
|
||||
pnpm dist # Both platforms
|
||||
```
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
"build": "electron-vite build",
|
||||
"dist": "electron-builder --mac --win --linux",
|
||||
"dist:mac": "electron-builder --mac --publish always",
|
||||
"dist:mac:arm64": "electron-builder --mac --arm64 --publish always",
|
||||
"dist:mac:x64": "electron-builder --mac --x64 --publish always",
|
||||
"dist:win": "electron-builder --win --publish always",
|
||||
"dist:linux": "electron-builder --linux --publish always",
|
||||
"preview": "electron-vite preview",
|
||||
|
|
|
|||
|
|
@ -435,25 +435,85 @@ export class SshConnectionManager extends EventEmitter {
|
|||
}
|
||||
|
||||
private async resolveRemoteProjectsPath(username: string): Promise<string> {
|
||||
// Try to resolve the remote home directory
|
||||
// SFTP doesn't have a direct "get home dir" call, so we try common paths
|
||||
// Prefer remote $HOME when available, then fall back to common paths.
|
||||
const remoteHome = await this.resolveRemoteHomeDirectory();
|
||||
const candidates = [
|
||||
...(remoteHome ? [path.posix.join(remoteHome, '.claude', 'projects')] : []),
|
||||
`/home/${username}/.claude/projects`,
|
||||
`/Users/${username}/.claude/projects`,
|
||||
`/root/.claude/projects`,
|
||||
];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
for (const candidate of [...new Set(candidates)]) {
|
||||
if (await this.provider.exists(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: try to read from environment via realpath of ~
|
||||
// Default to Linux convention
|
||||
// Fallback to inferred home-based path when we could resolve $HOME.
|
||||
if (remoteHome) {
|
||||
return path.posix.join(remoteHome, '.claude', 'projects');
|
||||
}
|
||||
|
||||
// Final fallback: Linux convention.
|
||||
return `/home/${username}/.claude/projects`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve remote user's home directory by querying `$HOME` over SSH.
|
||||
*/
|
||||
private async resolveRemoteHomeDirectory(): Promise<string | null> {
|
||||
if (!this.client) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const home = await this.execRemoteCommand('printf %s "$HOME"');
|
||||
const normalized = home.trim();
|
||||
return normalized.startsWith('/') ? normalized : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command on the connected SSH host and return stdout.
|
||||
*/
|
||||
private async execRemoteCommand(command: string): Promise<string> {
|
||||
const client = this.client;
|
||||
if (!client) {
|
||||
throw new Error('SSH client is not connected');
|
||||
}
|
||||
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
client.exec(command, (err, stream) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
stream.on('data', (chunk: Buffer | string) => {
|
||||
stdout += chunk.toString();
|
||||
});
|
||||
|
||||
stream.stderr.on('data', (chunk: Buffer | string) => {
|
||||
stderr += chunk.toString();
|
||||
});
|
||||
|
||||
stream.on('close', (code: number | null) => {
|
||||
if (code === 0) {
|
||||
resolve(stdout);
|
||||
return;
|
||||
}
|
||||
reject(new Error(stderr.trim() || `Remote command failed with exit code ${code}`));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private handleDisconnect(): void {
|
||||
if (this.state === 'disconnected') return;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue