diff --git a/BUILDING.md b/BUILDING.md index 9e0106ce..04531f01 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,6 +1,6 @@ # Building TimeSafari -This guide explains how to build TimeSafari for different platforms using our unified build scripts. +This guide explains how to build TimeSafari for different platforms. ## Prerequisites @@ -9,54 +9,7 @@ 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 -``` +- For desktop builds: Additional build tools based on your OS ## Forks @@ -77,104 +30,18 @@ Install dependencies: 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 +## Web Dev Locally ```bash npm run dev ``` -### Web Build for Server +## Web Build for Server 1. Run the production build: ```bash + rm -rf dist npm run build:web ``` @@ -182,278 +49,57 @@ resolve: { 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: + You'll likely want to use test locations for the Endorser & image & partner servers; see "DEFAULT_ENDORSER_API_SERVER" & "DEFAULT_IMAGE_API_SERVER" & "DEFAULT_PARTNER_API_SERVER" below. ```bash - npm install -g @capacitor/cli - ``` - -2. Add Electron platform: - - ```bash - npx cap add electron + npm run serve ``` -### 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: +### Compile and minify for test & production -```bash -# Build web assets -npm run build:capacitor +* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations. -# Sync with Capacitor -npx cap sync electron +* `npx prettier --write ./sw_scripts/` -# Build Electron app -npx cap build electron -``` +* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`. -### 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 +* Run a build to make sure package-lock version is updated, linting works, etc: `npm install && npm run build` -- **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 +* Commit everything (since the commit hash is used the app). -Prerequisites: Android Studio with Java SDK installed +* Put the commit hash in the changelog (which will help you remember to bump the version in the step later). -#### Complete Build Process +* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 1.0.2 && git push origin 1.0.2`. -Use the unified Android build script: +* For test, build the app (because test server is not yet set up to build): ```bash -./scripts/build-android.sh +TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app VITE_PASSKEYS_ENABLED=true npm run build:web ``` -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 - ``` + ... and transfer to the test server: -3. Generate assets: ```bash - npx capacitor-assets generate --android - ``` - -4. Open in Android Studio: - ```bash - npx cap open android + rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safari ``` -#### Console Build +(Let's replace that with a .env.development or .env.staging file.) -For building from the console: +(Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.) -```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 - ``` +* For prod, get on the server and run the correct build: -5. Build and run on simulator or device using Xcode + ... and log onto the server: -#### Release Process + * `pkgx +npm sh` -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 + * `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 1.0.2 && npm install && npm run build:web && cd -` -## Testing + (The plain `npm run build:web` uses the .env.production file.) -### Complete Test Suite +* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev-2 && mv crowd-funder-for-time-pwa/dist time-safari/` -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 -``` +* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, commit, and push. Also record what version is on production. ## Docker Deployment @@ -556,111 +202,280 @@ docker run -d \ - Check nginx configuration - Verify caching settings -## Configuration +## Desktop Build (Electron) -### Deep Links +### Linux Build -#### Android Configuration +1. Build the electron app in production mode: -You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file: - - ```xml - - - - - - + ```bash + npm run build:electron-prod ``` -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. +2. Package the Electron app for Linux: -#### iOS Configuration + ```bash + # For AppImage (recommended) + npm run electron:build-linux -For iOS deep links, configure the URL scheme in Xcode: + # For .deb package + npm run electron:build-linux-deb + ``` -1. Open the project in Xcode -2. Select your app target -3. Go to Info tab -4. Add URL Types with scheme `timesafari` +3. The packaged applications will be in `dist-electron-packages/`: + - AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage` + - DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb` -## Troubleshooting +### macOS Build -### Common Issues +1. Build the electron app in production mode: -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 + ```bash + npm run build:web + npm run build:electron + npm run electron:build-mac + ``` -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` +2. Package the Electron app for macOS: -3. **Permission Issues** - - Make scripts executable: `chmod +x scripts/*.sh` - - Check file permissions on build directories + ```bash + # For Intel Macs + npm run electron:build-mac -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 + # For Universal build (Intel + Apple Silicon) + npm run electron:build-mac-universal + ``` -### Getting Help +3. The packaged applications will be in `dist-electron-packages/`: + - `.app` bundle: `TimeSafari.app` + - `.dmg` installer: `TimeSafari-x.x.x.dmg` + - `.zip` archive: `TimeSafari-x.x.x-mac.zip` -- 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` +### Code Signing and Notarization (macOS) -### Platform Support Matrix +For public distribution on macOS, you need to code sign and notarize your app: -| 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) | +1. Set up environment variables: + ```bash + export CSC_LINK=/path/to/your/certificate.p12 + export CSC_KEY_PASSWORD=your_certificate_password + export APPLE_ID=your_apple_id + export APPLE_ID_PASSWORD=your_app_specific_password + ``` -## Platform Service Architecture +2. Build with signing: + ```bash + npm run electron:build-mac + ``` -TimeSafari uses a unified platform service architecture that works across all platforms: +### Running the Packaged App -### Platform Detection +- **Linux**: + - AppImage: Make executable and run + ```bash + chmod +x dist-electron-packages/TimeSafari-*.AppImage + ./dist-electron-packages/TimeSafari-*.AppImage + ``` + - DEB: Install and run + ```bash + sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb + timesafari + ``` + +- **macOS**: + - `.app` bundle: Double-click `TimeSafari.app` in Finder + - `.dmg` installer: + 1. Double-click the `.dmg` file + 2. Drag the app to your Applications folder + 3. Launch from Applications + - `.zip` archive: + 1. Extract the `.zip` file + 2. Move `TimeSafari.app` to your Applications folder + 3. Launch from Applications + + Note: If you get a security warning when running the app: + 1. Right-click the app + 2. Select "Open" + 3. Click "Open" in the security dialog + +### Development Testing + +For testing the Electron build before packaging: -The `CapacitorPlatformService` automatically detects the platform and adjusts capabilities: +```bash +# Build and run in development mode (includes DevTools) +npm run electron:dev -```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, - }; -} +# Build in production mode and test +npm run build:electron-prod && npm run electron:start ``` -### Unified Database Layer +## Mobile Builds (Capacitor) + +### 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. -All platforms use the same SQLite database through Capacitor plugins: +#### Each Release -- **Mobile**: `@capacitor-community/sqlite` plugin -- **Desktop**: Same plugin via Capacitor Electron -- **Web**: IndexedDB fallback with absurd-sql +0. First time (or if dependencies change): + + - `pkgx +rubygems.org sh` + + - ... and you may have to fix these, especially with pkgx: + + ```bash + gem_path=$(which gem) + shortened_path="${gem_path:h:h}" + export GEM_HOME=$shortened_path + export GEM_PATH=$shortened_path + ``` + +1. Build the web assets & update ios: + + ```bash + rm -rf dist + npm run build:web + npm run build:capacitor + npx cap sync ios + ``` + + - If that fails with "Could not find..." then look at the "gem_path" instructions above. + +3. Copy the assets: + + ```bash + # It makes no sense why capacitor-assets will not run without these but it actually changes the contents. + 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 + npx capacitor-assets generate --ios + ``` + +4. Bump the version to match Android & package.json: + + ``` + 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 - + # Unfortunately this edits Info.plist directly. + #xcrun agvtool new-marketing-version 0.4.5 + ``` -### Feature Parity +5. Open the project in Xcode: -The same Capacitor plugins work across all platforms: + ```bash + npx cap open ios + ``` + +6. Use Xcode to build and run on simulator or device. + + * Select Product -> Destination with some Simulator version. Then click the run arrow. + +7. Release + + * Someday: Under "General" we want to rename a bunch of things to "Time Safari" + * Choose Product -> Destination -> Any iOS Device + * Choose Product -> Archive + * This will trigger a build and take time, needing user's "login" keychain password (user's login password), repeatedly. + * If it fails with `building for 'iOS', but linking in dylib (.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run XCode outside that terminal (ie. not with `npx cap open ios`). + * Click Distribute -> App Store Connect + * In AppStoreConnect, add the build to the distribution: remove the current build with the "-" when you hover over it, then "Add Build" with the new build. + * May have to go to App Review, click Submission, then hover over the build and click "-". + * It can take 15 minutes for the build to show up in the list of builds. + * You'll probably have to "Manage" something about encryption, disallowed in France. + * Then "Save" and "Add to Review" and "Resubmit to App Review". + +### Android Build + +Prerequisites: Android Studio with Java SDK installed + +1. Build the web assets: + + ```bash + rm -rf dist + npm run build:web + npm run build:capacitor + ``` + +2. Update Android project with latest build: + + ```bash + npx cap sync android + ``` + +3. Copy the assets + + ```bash + npx capacitor-assets generate --android + ``` + +4. Bump version to match iOS & package.json: android/app/build.gradle + +5. Open the project in Android Studio: + + ```bash + npx cap open android + ``` + +6. Use Android Studio to build and run on emulator or device. + +## Android Build from the console + + ```bash + cd android + ./gradlew clean + ./gradlew build -Dlint.baselines.continue=true + cd - + ``` + +... or, to create the `aab` file, `bundle` instead of `build`: + + ```bash + ./gradlew bundleDebug -Dlint.baselines.continue=true + ``` + +... or, to create a signed release: + + * Setup by adding the app/gradle.properties.secrets file (see properties at top of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file + * In app/build.gradle, bump the versionCode and maybe the versionName + * Then `bundleRelease`: + + ```bash + cd android + ./gradlew bundleRelease -Dlint.baselines.continue=true + cd - + ``` + + ... and find your `aab` file at app/build/outputs/bundle/release + +At play.google.com/console: + +- Go to the Testing Track (eg. Closed). +- Click "Create new release". +- Upload the `aab` file. +- Hit "Next". +- Save, go to "Publishing Overview" as prompted, and click "Send changes for review". + +- Note that if you add testers, you have to go to "Publishing Overview" and send those changes or your (closed) testers won't see it. + + +## Android Configuration for deep links + +You must add the following intent filter to the `android/app/src/main/AndroidManifest.xml` file: + + ```xml + + + + + + + ``` -- File system operations -- Camera access -- SQLite database -- Deep linking -- Sharing functionality +... though when we tried that most recently it failed to 'build' the APK with: http(s) scheme and host attribute are missing, but are required for Android App Links [AppLinkUrlError]