mirror of
https://github.com/spacedriveapp/spacedrive.git
synced 2026-02-19 15:19:09 -05:00
Update macOS entitlements and post-build script
This commit is contained in:
129
AGENTS.md
129
AGENTS.md
@@ -410,31 +410,120 @@ impl Job for FileCopyJob {
|
||||
|
||||
### Documentation
|
||||
|
||||
- Module docs: `//!` at top of file
|
||||
- Public items: `///` with examples
|
||||
- Focus on why, not what
|
||||
- Track future work in GitHub issues, not code comments
|
||||
**Core principle:** Explain WHY, not WHAT. Keep comments as short as possible. One sentence explaining rationale beats a paragraph restating code.
|
||||
|
||||
**Module docs (`//!`):**
|
||||
- Add a title with `#` for the module name
|
||||
- Explain what the module does in plain language (not bullet points)
|
||||
- Include design rationale naturally in prose
|
||||
- Add runnable code examples showing usage
|
||||
|
||||
````rust
|
||||
//! File sharing operations.
|
||||
//! # File Sharing System
|
||||
//!
|
||||
//! Handles creating, revoking, and managing file shares.
|
||||
|
||||
/// Creates a new file share with the specified recipient.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let output = share_file(ShareFileInput {
|
||||
/// file_id: 123,
|
||||
/// recipient: "user@example.com".to_string(),
|
||||
/// }).await?;
|
||||
/// ```
|
||||
pub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput> {
|
||||
// Implementation
|
||||
}
|
||||
//! `core::ops::files::share` provides temporary file sharing via signed URLs.
|
||||
//! Share links expire after 7 days by default to prevent indefinite access to
|
||||
//! private files. UUID v5 deterministic IDs ensure the same file generates
|
||||
//! consistent share URLs across devices without coordination.
|
||||
//!
|
||||
//! ## Example
|
||||
//! ```rust,no_run
|
||||
//! use spacedrive_core::ops::files::share::{ShareFileAction, ShareFileInput};
|
||||
//!
|
||||
//! let input = ShareFileInput { file_id: 123, recipient: "user@example.com" };
|
||||
//! let output = ShareFileAction::run(input, &ctx).await?;
|
||||
//! ```
|
||||
````
|
||||
|
||||
**Function docs (`///`):**
|
||||
- First line: brief one-liner
|
||||
- Second paragraph: explain design rationale and why this exists
|
||||
- Document error handling philosophy when relevant
|
||||
- Explain non-obvious behavior and platform differences
|
||||
|
||||
```rust
|
||||
/// Creates a share link with automatic expiration.
|
||||
///
|
||||
/// Share links use signed JWTs so the daemon can validate them without
|
||||
/// database lookups on every request. Expiration is enforced server-side
|
||||
/// to prevent timezone manipulation. Recipients without library access
|
||||
/// get read-only access to the specific file only.
|
||||
///
|
||||
/// Returns `ShareError::PermissionDenied` if the file is private and
|
||||
/// the recipient isn't a library member. The share is still created
|
||||
/// but marked inactive for audit logging.
|
||||
pub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput>
|
||||
```
|
||||
|
||||
**Inline comments:**
|
||||
- Delete comments that restate obvious code
|
||||
- Explain WHY for decisions, not WHAT the code does
|
||||
- Use one sentence when possible
|
||||
- Only expand for truly non-obvious consequences
|
||||
|
||||
```rust
|
||||
// Good: explains WHY
|
||||
// Lowercase for case-insensitive search matching.
|
||||
let ext = path.extension().map(|e| e.to_lowercase());
|
||||
|
||||
// Bad: restates code
|
||||
// Extract file extension and convert to lowercase
|
||||
let ext = path.extension().map(|e| e.to_lowercase());
|
||||
|
||||
// Good: explains consequence
|
||||
// Preserve ephemeral UUIDs so tags attached during browsing survive promotion to managed location.
|
||||
let uuid = ephemeral_cache.get(path).unwrap_or_else(|| Uuid::new_v4());
|
||||
|
||||
// Bad: verbose explanation of obvious behavior
|
||||
// UUID assignment strategy:
|
||||
// 1. First check if there's an ephemeral UUID
|
||||
// 2. If not, generate a new one
|
||||
let uuid = ephemeral_cache.get(path).unwrap_or_else(|| Uuid::new_v4());
|
||||
```
|
||||
|
||||
**Error handling comments:**
|
||||
Explain strategy and recovery, not just "log and continue".
|
||||
|
||||
```rust
|
||||
// Good: explains recovery
|
||||
// Best-effort: continue with remaining moves, stale paths cleaned up on next reindex.
|
||||
Err(e) => ctx.log(format!("Failed to move: {}", e)),
|
||||
|
||||
// Bad: states the obvious
|
||||
// Log error but continue
|
||||
Err(e) => ctx.log(format!("Failed to move: {}", e)),
|
||||
```
|
||||
|
||||
**Platform-specific comments:**
|
||||
Explain consequences, not implementation blockers.
|
||||
|
||||
```rust
|
||||
// Good: explains why and fallback
|
||||
#[cfg(windows)]
|
||||
pub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {
|
||||
// Windows file indices are unstable across reboots; fall back to path-only matching.
|
||||
None
|
||||
}
|
||||
|
||||
// Bad: over-explains implementation details
|
||||
#[cfg(windows)]
|
||||
pub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {
|
||||
// Windows doesn't have inodes.
|
||||
// The method `file_index()` is unstable (issue #63010).
|
||||
// Returning None is safe as the field is Optional.
|
||||
None
|
||||
}
|
||||
```
|
||||
|
||||
**Never use:**
|
||||
- Placeholder comments ("for now", "TODO: extract this later")
|
||||
- Markdown formatting (`**bold**`, `_italic_`) in code comments
|
||||
- ASCII diagrams (put those in `/docs/` if needed)
|
||||
- Section divider comments (`// ========== Section ==========`)
|
||||
- Comments explaining removed code during refactors
|
||||
|
||||
Track future work in GitHub issues, not code comments.
|
||||
|
||||
### Formatting
|
||||
|
||||
Run `cargo fmt` before committing. Tabs for indentation. No emojis.
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"tauri": "bunx tauri",
|
||||
"tauri:dev": "bunx tauri dev",
|
||||
"tauri:dev:no-watch": "bunx tauri dev --no-watch",
|
||||
"tauri:build": "bunx tauri build"
|
||||
"tauri:build": "bunx tauri build && ./scripts/fix-daemon-entitlements.sh ../../target/release/bundle/macos/Spacedrive.app"
|
||||
},
|
||||
"dependencies": {
|
||||
"@phosphor-icons/react": "^2.1.0",
|
||||
|
||||
33
apps/tauri/scripts/fix-daemon-entitlements.sh
Executable file
33
apps/tauri/scripts/fix-daemon-entitlements.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# This script fixes the daemon entitlements in the bundled macOS app
|
||||
# It removes the app-sandbox entitlement which causes the daemon to crash
|
||||
|
||||
BUNDLE_PATH="$1"
|
||||
|
||||
if [ -z "$BUNDLE_PATH" ]; then
|
||||
echo "Usage: $0 <path-to-app-bundle>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DAEMON_PATH="$BUNDLE_PATH/Contents/MacOS/sd-daemon"
|
||||
ENTITLEMENTS_PATH="$(dirname "$0")/../src-tauri/DaemonEntitlements.plist"
|
||||
|
||||
if [ ! -f "$DAEMON_PATH" ]; then
|
||||
echo "Error: Daemon not found at $DAEMON_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$ENTITLEMENTS_PATH" ]; then
|
||||
echo "Error: DaemonEntitlements.plist not found at $ENTITLEMENTS_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Re-signing daemon with correct entitlements..."
|
||||
codesign --force --sign - \
|
||||
--entitlements "$ENTITLEMENTS_PATH" \
|
||||
--options runtime \
|
||||
"$DAEMON_PATH"
|
||||
|
||||
echo "✓ Daemon re-signed successfully"
|
||||
23
apps/tauri/src-tauri/DaemonEntitlements.plist
Normal file
23
apps/tauri/src-tauri/DaemonEntitlements.plist
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- NO app-sandbox for daemon - it needs full filesystem access -->
|
||||
|
||||
<!-- Network access for daemon communication -->
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
|
||||
<!-- Allow loading unsigned libraries (for bundled frameworks) -->
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
|
||||
<!-- Hardened runtime -->
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -2,9 +2,7 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- Allow sandboxed app to run -->
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<!-- NO app-sandbox - Spacedrive needs full filesystem access as a file manager -->
|
||||
|
||||
<!-- Network access for daemon communication -->
|
||||
<key>com.apple.security.network.client</key>
|
||||
@@ -12,22 +10,14 @@
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
|
||||
<!-- File access -->
|
||||
<key>com.apple.security.files.user-selected.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.bookmarks.app-scope</key>
|
||||
<true/>
|
||||
|
||||
<!-- Allow launching daemon process -->
|
||||
<!-- Allow launching daemon process and loading frameworks -->
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
|
||||
<!-- Background execution (for daemon) -->
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>com.spacedrive.desktop</string>
|
||||
</array>
|
||||
<!-- Hardened runtime -->
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
Reference in New Issue
Block a user