Files
crowd-funder-from-jason/BUILDING.md
Matthew Raymer ea0f49d5c3 WIP: add Electron platform configuration to Capacitor
- Add electron platform section to capacitor.config.json
- Configure deep linking with timesafari:// scheme
- Set up build options for macOS, Windows, and Linux
- Configure output directory and file inclusion
- Add platform-specific build targets (DMG, NSIS, AppImage)
- Support both x64 and arm64 architectures for macOS
- Set appropriate app categories for each platform

This enables building TimeSafari as a native desktop application
using Capacitor's Electron platform while maintaining existing
mobile and web functionality.
2025-06-25 12:50:46 +00:00

667 lines
16 KiB
Markdown

# Building TimeSafari
This guide explains how to build TimeSafari for different platforms using our unified build scripts.
## Prerequisites
For a quick dev environment setup, use [pkgx](https://pkgx.dev).
- Node.js (LTS version recommended)
- npm (comes with Node.js)
- Git
- For mobile builds: Android Studio (Android) or Xcode (iOS)
- For desktop builds: Capacitor Electron platform
## Unified Build Scripts
TimeSafari now uses unified build scripts that automatically handle environment variables, logging, error handling, and timing. All scripts are located in the `scripts/` directory and use a common utilities library.
### Script Features
- **Automatic Environment Setup**: Each script sets the correct environment variables for its build type
- **Rich Logging**: Colored, timestamped output with different log levels
- **Error Handling**: Proper exit codes and graceful failure recovery
- **Timing**: Automatic execution time tracking for each step
- **Validation**: Checks for required dependencies and files
- **CLI Options**: `--help`, `--verbose`, `--env` flags for all scripts
### Available Scripts
| Script | Purpose | Command |
|--------|---------|---------|
| `capacitor-dev.sh` | Capacitor development | `./scripts/capacitor-dev.sh` |
| `capacitor-build.sh` | Capacitor build | `./scripts/build-capacitor.sh` |
| `web-dev.sh` | Web development | `./scripts/web-dev.sh` |
| `web-build.sh` | Web build | `./scripts/build-web.sh` |
### Environment Variables
All scripts automatically set the correct environment variables for their build type:
| Build Type | VITE_PLATFORM | VITE_PWA_ENABLED | VITE_DISABLE_PWA | NODE_ENV |
|------------|---------------|------------------|------------------|----------|
| `capacitor` | capacitor | false | true | - |
| `web` | web | true | false | - |
### CLI Options
All scripts support these options:
```bash
# Show help
./scripts/build-capacitor.sh --help
# Enable verbose logging
./scripts/build-capacitor.sh --verbose
# Show environment variables
./scripts/build-capacitor.sh --env
```
## Forks
If you have forked this to make your own app, you'll want to customize the iOS & Android files. You can either edit existing ones, or you can remove the `ios` and `android` directories and regenerate them before the `npx cap sync` step in each setup.
```bash
npx cap add android
npx cap add ios
```
You'll also want to edit the deep link configuration (see below).
## Initial Setup
Install dependencies:
```bash
npm install
```
## Package Management
TimeSafari uses a mixed package management approach, combining npm and JSR (JavaScript Registry) for optimal dependency management.
### JSR Integration
Some packages are installed via JSR for better ESM support and modern TypeScript compatibility:
```bash
# Install JSR packages
npx jsr add @nostr/tools
```
### Package Migration History
#### nostr-tools → @nostr/tools
**Date**: June 2025
**Reason**: Resolved Vite/Rollup build issues with deep imports
**Before** (npm):
```typescript
import { finalizeEvent } from "nostr-tools/lib/cjs/index.js";
import { accountFromExtendedKey } from "nostr-tools/lib/cjs/nip06.js";
```
**After** (JSR):
```typescript
import { finalizeEvent } from "@nostr/tools";
import { accountFromExtendedKey } from "@nostr/tools/nip06";
```
**Benefits**:
- ✅ Proper ESM support
- ✅ No deep import issues with Vite/Rollup
- ✅ Better TypeScript compatibility
- ✅ Modern package structure
### Current Package Strategy
- **npm**: Primary package manager for most dependencies
- **JSR**: Used for packages with better ESM support or modern alternatives
- **Mixed approach**: Allows using the best package for each dependency
### When to Use JSR
Consider using JSR for:
- Packages with ESM/CJS compatibility issues in npm
- Modern TypeScript-first packages
- Packages that work better with modern bundlers
- New dependencies where JSR has a better alternative
### Vite Configuration
The build system is configured to handle both npm and JSR packages:
```typescript
// vite.config.common.mts
resolve: {
alias: {
'@nostr/tools': path.resolve(__dirname, 'node_modules/@nostr/tools'),
'@nostr/tools/nip06': path.resolve(__dirname, 'node_modules/@nostr/tools/nip06'),
}
}
```
### Troubleshooting Package Issues
1. **Build failures with deep imports**
- Check if package has ESM/CJS compatibility issues
- Consider JSR alternative if available
- Update Vite configuration if needed
2. **TypeScript errors**
- Ensure proper type definitions are available
- Check package exports in package.json
- Verify import paths match package structure
3. **Mixed package manager issues**
- Keep package.json and node_modules in sync
- Use `npm install` after JSR package additions
- Check for conflicting package versions
## Web Development
### Local Development
```bash
npm run dev
```
### Web Build for Server
1. Run the production build:
```bash
npm run build:web
```
The built files will be in the `dist` directory.
2. To test the production build locally:
```bash
npm run serve
```
### Environment Configuration
For different environments, create `.env` files:
```bash
# .env.development
VITE_APP_SERVER=https://dev.timesafari.app
VITE_DEFAULT_ENDORSER_API_SERVER=https://dev-api.endorser.ch
VITE_DEFAULT_IMAGE_API_SERVER=https://dev-image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=https://dev-partner-api.endorser.ch
VITE_DEFAULT_PUSH_SERVER=https://dev.timesafari.app
VITE_PASSKEYS_ENABLED=true
# .env.production
VITE_APP_SERVER=https://timesafari.app
VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch
VITE_DEFAULT_IMAGE_API_SERVER=https://image-api.timesafari.app
VITE_DEFAULT_PARTNER_API_SERVER=https://partner-api.endorser.ch
VITE_DEFAULT_PUSH_SERVER=https://timesafari.app
VITE_PASSKEYS_ENABLED=true
```
## Desktop Build (Capacitor Electron)
### Prerequisites
1. Install Capacitor CLI:
```bash
npm install -g @capacitor/cli
```
2. Add Electron platform:
```bash
npx cap add electron
```
### Development
For development with automatic environment setup:
```bash
# Build web assets
npm run build:capacitor
# Sync with Capacitor
npx cap sync electron
# Open in Electron
npx cap open electron
```
### Production Build
For production builds:
```bash
# Build web assets
npm run build:capacitor
# Sync with Capacitor
npx cap sync electron
# Build Electron app
npx cap build electron
```
### Packaging
Capacitor Electron uses electron-builder for packaging. Configure the build in `capacitor.config.json`:
```json
{
"plugins": {
"ElectronBuilder": {
"buildOptions": {
"appId": "app.timesafari.app",
"productName": "TimeSafari",
"directories": {
"output": "dist-electron-packages"
},
"files": [
"dist/**/*",
"electron/**/*"
],
"linux": {
"target": ["AppImage", "deb"],
"category": "Office"
},
"mac": {
"target": ["dmg", "zip"],
"category": "public.app-category.productivity"
},
"win": {
"target": ["nsis", "portable"]
}
}
}
}
}
```
### Running the Packaged App
- **Linux**: AppImage files are self-contained executables
- **macOS**: `.app` bundles can be dragged to Applications folder
- **Windows**: `.exe` installers or portable executables
## Mobile Builds (Capacitor)
### Android Build
Prerequisites: Android Studio with Java SDK installed
#### Complete Build Process
Use the unified Android build script:
```bash
./scripts/build-android.sh
```
This script automatically:
1. Sets up environment variables for Capacitor
2. Cleans previous builds
3. Builds web assets
4. Builds Capacitor version
5. Cleans and builds Gradle project
6. Syncs with Capacitor
7. Generates assets
8. Opens Android Studio
#### Manual Steps (if needed)
If you need to run individual steps:
1. Build the web assets:
```bash
npm run build:web
npm run build:capacitor
```
2. Update Android project:
```bash
npx cap sync android
```
3. Generate assets:
```bash
npx capacitor-assets generate --android
```
4. Open in Android Studio:
```bash
npx cap open android
```
#### Console Build
For building from the console:
```bash
cd android
./gradlew clean
./gradlew build -Dlint.baselines.continue=true
cd -
```
For creating an `aab` file:
```bash
cd android
./gradlew bundleDebug -Dlint.baselines.continue=true
cd -
```
For creating a signed release:
1. Setup signing configuration in `app/gradle.properties.secrets`
2. Add signing key file `app/time-safari-upload-key-pkcs12.jks`
3. Update version in `app/build.gradle`
4. Build release:
```bash
cd android
./gradlew bundleRelease -Dlint.baselines.continue=true
cd -
```
The `aab` file will be at `app/build/outputs/bundle/release`.
### iOS Build
Prerequisites: macOS with Xcode installed
#### First-time iOS Configuration
- Generate certificates inside Xcode
- Right-click on App and under Signing & Capabilities set the Team
#### Build Process
1. Build the web assets & update iOS:
```bash
npm run build:web
npm run build:capacitor
npx cap sync ios
```
2. Generate assets:
```bash
# Create required directories
mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset
echo '{"images":[]}' > ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json
mkdir -p ios/App/App/Assets.xcassets/Splash.imageset
echo '{"images":[]}' > ios/App/App/Assets.xcassets/Splash.imageset/Contents.json
# Generate assets
npx capacitor-assets generate --ios
```
3. Update version to match Android & package.json:
```bash
cd ios/App
xcrun agvtool new-version 35
perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.2;/g" App.xcodeproj/project.pbxproj
cd -
```
4. Open in Xcode:
```bash
npx cap open ios
```
5. Build and run on simulator or device using Xcode
#### Release Process
1. Choose Product -> Destination -> Any iOS Device
2. Choose Product -> Archive
3. Click Distribute -> App Store Connect
4. In App Store Connect, add the build to the distribution
## Testing
### Complete Test Suite
Run all tests with automatic environment setup:
```bash
./scripts/test-all.sh
```
### Mobile Tests
Run mobile-specific tests:
```bash
./scripts/test-mobile.sh
```
### Environment Testing
Test environment variable handling:
```bash
./scripts/test-env.sh
```
## Docker Deployment
The application can be containerized using Docker for consistent deployment across environments.
### Prerequisites
- Docker installed on your system
- Docker Compose (optional, for multi-container setups)
### Building the Docker Image
1. Build the Docker image:
```bash
docker build -t timesafari:latest .
```
2. For development builds with specific environment variables:
```bash
docker build --build-arg NODE_ENV=development -t timesafari:dev .
```
### Running the Container
1. Run the container:
```bash
docker run -d -p 80:80 timesafari:latest
```
2. For development with hot-reloading:
```bash
docker run -d -p 80:80 -v $(pwd):/app timesafari:dev
```
### Using Docker Compose
Create a `docker-compose.yml` file:
```yaml
version: '3.8'
services:
timesafari:
build: .
ports:
- "80:80"
environment:
- NODE_ENV=production
restart: unless-stopped
```
Run with Docker Compose:
```bash
docker-compose up -d
```
### Production Deployment
For production deployment, consider the following:
1. Use specific version tags instead of 'latest'
2. Implement health checks
3. Configure proper logging
4. Set up reverse proxy with SSL termination
5. Use Docker secrets for sensitive data
Example production deployment:
```bash
# Build with specific version
docker build -t timesafari:1.0.0 .
# Run with production settings
docker run -d \
--name timesafari \
-p 80:80 \
--restart unless-stopped \
-e NODE_ENV=production \
timesafari:1.0.0
```
### Troubleshooting Docker
1. **Container fails to start**
- Check logs: `docker logs <container_id>`
- Verify port availability
- Check environment variables
2. **Build fails**
- Ensure all dependencies are in package.json
- Check Dockerfile syntax
- Verify build context
3. **Performance issues**
- Monitor container resources: `docker stats`
- Check nginx configuration
- Verify caching settings
## Configuration
### Deep Links
#### Android Configuration
You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file:
```xml
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="timesafari" />
</intent-filter>
```
Note: When using `timesafari://` scheme, you may encounter build errors about missing http(s) scheme and host attributes. This is expected for custom URL schemes.
#### iOS Configuration
For iOS deep links, configure the URL scheme in Xcode:
1. Open the project in Xcode
2. Select your app target
3. Go to Info tab
4. Add URL Types with scheme `timesafari`
## Troubleshooting
### Common Issues
1. **Environment Variables Not Set**
- Use `--env` flag to check current environment: `./scripts/build-capacitor.sh --env`
- Verify `.env` file exists and is properly formatted
- Check script output for environment setup messages
2. **Build Failures**
- Use `--verbose` flag for detailed logging: `./scripts/build-capacitor.sh --verbose`
- Check prerequisites are installed
- Verify all dependencies are installed: `npm install`
3. **Permission Issues**
- Make scripts executable: `chmod +x scripts/*.sh`
- Check file permissions on build directories
4. **Platform-Specific Issues**
- **Android**: Verify Android Studio and SDK are properly configured
- **iOS**: Ensure Xcode and certificates are set up correctly
- **Electron**: Check Capacitor Electron platform installation
### Getting Help
- Check script help: `./scripts/build-capacitor.sh --help`
- Review script documentation in `scripts/README.md`
- Test environment setup: `./scripts/test-env.sh`
- Test common utilities: `./scripts/test-common.sh`
### Platform Support Matrix
| Platform | Mode | PWA Enabled | Native Features | Notes |
|----------|------|-------------|-----------------|-------|
| `web` | web | true | false | Standard web browser |
| `capacitor` | capacitor | false | true | Mobile app (iOS/Android) |
| `electron` | capacitor | false | true | Desktop app (via Capacitor Electron) |
## Platform Service Architecture
TimeSafari uses a unified platform service architecture that works across all platforms:
### Platform Detection
The `CapacitorPlatformService` automatically detects the platform and adjusts capabilities:
```typescript
getCapabilities(): PlatformCapabilities {
const platform = Capacitor.getPlatform();
const isElectron = platform === "electron";
return {
hasFileSystem: true,
hasCamera: true,
isMobile: !isElectron, // false for Electron, true for mobile
isIOS: platform === "ios",
hasFileDownload: isElectron, // Electron can download files directly
needsFileHandlingInstructions: !isElectron, // Mobile needs instructions
isNativeApp: true,
};
}
```
### Unified Database Layer
All platforms use the same SQLite database through Capacitor plugins:
- **Mobile**: `@capacitor-community/sqlite` plugin
- **Desktop**: Same plugin via Capacitor Electron
- **Web**: IndexedDB fallback with absurd-sql
### Feature Parity
The same Capacitor plugins work across all platforms:
- File system operations
- Camera access
- SQLite database
- Deep linking
- Sharing functionality