From a2840675221c55344c3ccb459d8ea982910f1ada Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Thu, 14 Aug 2025 07:22:26 +0000 Subject: [PATCH] feat(assets): standardize asset configuration with capacitor-assets - Replace manual ImageMagick scripts with official capacitor-assets toolchain - Consolidate duplicate asset sources to single resources/ directory - Implement comprehensive asset configuration schema and validation - Add CI safeguards for asset validation and platform asset detection - Convert capacitor.config.json to TypeScript format - Pin Node.js version for deterministic builds - Remove legacy manual asset generation scripts: * generate-icons.sh, generate-ios-assets.sh, generate-android-icons.sh * check-android-resources.sh, check-ios-resources.sh * purge-generated-assets.sh - Add new asset management commands: * assets:config - generate/update configurations * assets:validate - validate configurations * assets:clean - clean generated assets (dev only) * build:native - build with asset generation - Create GitHub Actions workflow for asset validation - Update documentation with new asset management workflow This standardization eliminates asset duplication, improves build reliability, and provides a maintainable asset management system using Capacitor defaults. Breaking Changes: Manual asset generation scripts removed Migration: Assets now sourced from resources/ directory only CI: Automated validation prevents committed platform assets --- .cursor/rules/asset_configuration.mdc | 32 ++ .github/workflows/asset-validation.yml | 142 +++++++++ .gitignore | 12 + .node-version | 1 + .nvmrc | 1 + README.md | 45 ++- .../app/src/main/assets/capacitor.config.json | 2 +- assets/README.md | 2 - capacitor-assets.config.json | 38 +-- capacitor.config.ts | 116 +++++++ config/assets/capacitor-assets.config.json | 32 ++ config/assets/schema.json | 119 +++++++ doc/asset-migration-plan.md | 214 +++++++++++++ package.json | 4 + {assets => resources}/icon.png | Bin {assets => resources}/splash.png | Bin {assets => resources}/splash_dark.png | Bin scripts/assets-config.js | 174 +++++++++++ scripts/assets-validator.js | 218 +++++++++++++ scripts/check-android-resources.sh | 159 ---------- scripts/check-ios-resources.sh | 294 ------------------ scripts/generate-android-icons.sh | 107 ------- scripts/generate-icons.sh | 79 ----- scripts/generate-ios-assets.sh | 253 --------------- scripts/purge-generated-assets.sh | 67 ---- 25 files changed, 1125 insertions(+), 986 deletions(-) create mode 100644 .cursor/rules/asset_configuration.mdc create mode 100644 .github/workflows/asset-validation.yml create mode 100644 .node-version create mode 100644 .nvmrc delete mode 100644 assets/README.md create mode 100644 capacitor.config.ts create mode 100644 config/assets/capacitor-assets.config.json create mode 100644 config/assets/schema.json create mode 100644 doc/asset-migration-plan.md rename {assets => resources}/icon.png (100%) rename {assets => resources}/splash.png (100%) rename {assets => resources}/splash_dark.png (100%) create mode 100644 scripts/assets-config.js create mode 100644 scripts/assets-validator.js delete mode 100755 scripts/check-android-resources.sh delete mode 100755 scripts/check-ios-resources.sh delete mode 100755 scripts/generate-android-icons.sh delete mode 100755 scripts/generate-icons.sh delete mode 100755 scripts/generate-ios-assets.sh delete mode 100755 scripts/purge-generated-assets.sh diff --git a/.cursor/rules/asset_configuration.mdc b/.cursor/rules/asset_configuration.mdc new file mode 100644 index 00000000..916ecdd6 --- /dev/null +++ b/.cursor/rules/asset_configuration.mdc @@ -0,0 +1,32 @@ +--- +alwaysApply: true +--- +# Asset Configuration Directive +*Scope: Assets Only (icons, splashes, image pipelines) — not overall build orchestration* + +## Intent +- Version **asset configuration files** (optionally dev-time generated). +- **Do not** version platform asset outputs (Android/iOS/Electron); generate them **at build-time** with standard tools. +- Keep existing per-platform build scripts unchanged. + +## Source of Truth +- **Preferred (Capacitor default):** `resources/` as the single master source. +- **Alternative:** `assets/` is acceptable **only** if `capacitor-assets` is explicitly configured to read from it. +- **Never** maintain both `resources/` and `assets/` as parallel sources. Migrate and delete the redundant folder. + +## Config Files +- Live under: `config/assets/` (committed). +- Examples: + - `config/assets/capacitor-assets.config.json` (or the path the tool expects) + - `config/assets/android.assets.json` + - `config/assets/ios.assets.json` + - `config/assets/common.assets.yaml` (optional shared layer) +- **Dev-time generation allowed** for these configs; **build-time generation is forbidden**. + +## Build-Time Behavior +- Build generates platform assets (not configs) using the standard chain: + ```bash + npm run build:capacitor # web build via Vite (.mts) + npx cap sync + npx capacitor-assets generate # produces platform assets; not committed + # then platform-specific build steps diff --git a/.github/workflows/asset-validation.yml b/.github/workflows/asset-validation.yml new file mode 100644 index 00000000..72cd2be0 --- /dev/null +++ b/.github/workflows/asset-validation.yml @@ -0,0 +1,142 @@ +name: Asset Validation & CI Safeguards + +on: + pull_request: + paths: + - 'resources/**' + - 'config/assets/**' + - 'capacitor-assets.config.json' + - 'capacitor.config.ts' + - 'capacitor.config.json' + push: + branches: [main, develop] + paths: + - 'resources/**' + - 'config/assets/**' + - 'capacitor-assets.config.json' + - 'capacitor.config.ts' + - 'capacitor.config.json' + +jobs: + asset-validation: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Validate asset configuration + run: npm run assets:validate + + - name: Check for committed platform assets (Android) + run: | + if git ls-files -z android/app/src/main/res | grep -E '(AppIcon.*\.png|Splash.*\.png|mipmap-.*/ic_launcher.*\.png)' > /dev/null; then + echo "❌ Android platform assets found in VCS - these should be generated at build-time" + git ls-files -z android/app/src/main/res | grep -E '(AppIcon.*\.png|Splash.*\.png|mipmap-.*/ic_launcher.*\.png)' + exit 1 + fi + echo "✅ No Android platform assets committed" + + - name: Check for committed platform assets (iOS) + run: | + if git ls-files -z ios/App/App/Assets.xcassets | grep -E '(AppIcon.*\.png|Splash.*\.png)' > /dev/null; then + echo "❌ iOS platform assets found in VCS - these should be generated at build-time" + git ls-files -z ios/App/App/Assets.xcassets | grep -E '(AppIcon.*\.png|Splash.*\.png)' + exit 1 + fi + echo "✅ No iOS platform assets committed" + + - name: Test asset generation + run: | + echo "🧪 Testing asset generation workflow..." + npm run build:capacitor + npx cap sync + npx capacitor-assets generate --dry-run || npx capacitor-assets generate + echo "✅ Asset generation test completed" + + - name: Verify clean tree after build + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "❌ Dirty tree after build - asset configs were modified" + git status + git diff + exit 1 + fi + echo "✅ Build completed with clean tree" + + schema-validation: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Validate schema compliance + run: | + echo "🔍 Validating schema compliance..." + node -e " + const fs = require('fs'); + const config = JSON.parse(fs.readFileSync('capacitor-assets.config.json', 'utf8')); + const schema = JSON.parse(fs.readFileSync('config/assets/schema.json', 'utf8')); + + // Basic schema validation + if (!config.icon || !config.splash) { + throw new Error('Missing required sections: icon and splash'); + } + + if (!config.icon.source || !config.splash.source) { + throw new Error('Missing required source fields'); + } + + if (!/^resources\/.*\.(png|svg)$/.test(config.icon.source)) { + throw new Error('Icon source must be in resources/ directory'); + } + + if (!/^resources\/.*\.(png|svg)$/.test(config.splash.source)) { + throw new Error('Splash source must be in resources/ directory'); + } + + console.log('✅ Schema validation passed'); + " + + - name: Check source file existence + run: | + echo "📁 Checking source file existence..." + node -e " + const fs = require('fs'); + const config = JSON.parse(fs.readFileSync('capacitor-assets.config.json', 'utf8')); + + const requiredFiles = [ + config.icon.source, + config.splash.source + ]; + + if (config.splash.darkSource) { + requiredFiles.push(config.splash.darkSource); + } + + const missingFiles = requiredFiles.filter(file => !fs.existsSync(file)); + + if (missingFiles.length > 0) { + console.error('❌ Missing source files:', missingFiles); + process.exit(1); + } + + console.log('✅ All source files exist'); + " diff --git a/.gitignore b/.gitignore index a9f37e49..4202ef2a 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,10 @@ icons *.log +# Build outputs +dist/ +build/ + # Generated Android assets and resources (should be generated during build) android/app/src/main/assets/public/ @@ -64,6 +68,14 @@ android/app/src/main/res/drawable*/ android/app/src/main/res/mipmap*/ android/app/src/main/res/values/ic_launcher_background.xml +# Android generated assets (deny-listed in CI) +android/app/src/main/res/mipmap-*/ic_launcher*.png +android/app/src/main/res/drawable*/splash*.png + +# iOS generated assets (deny-listed in CI) +ios/App/App/Assets.xcassets/**/AppIcon*.png +ios/App/App/Assets.xcassets/**/Splash*.png + # Keep these Android configuration files in version control: # - android/app/src/main/assets/capacitor.plugins.json # - android/app/src/main/res/values/strings.xml diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..a9d08739 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +18.19.0 diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..a9d08739 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.19.0 diff --git a/README.md b/README.md index 6992ba09..efc9b1ad 100644 --- a/README.md +++ b/README.md @@ -136,11 +136,50 @@ const apiUrl = `${APP_SERVER}/api/claim/123`; See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions. -## Icons +## Asset Management -Application icons are in the `assets` directory, processed by the `capacitor-assets` command. +TimeSafari uses a standardized asset configuration system for consistent +icon and splash screen generation across all platforms. -To add a Font Awesome icon, add to fontawesome.ts and reference with `font-awesome` element and `icon` attribute with the hyphenated name. +### Asset Sources + +- **Single source of truth**: `resources/` directory (Capacitor default) +- **Source files**: `icon.png`, `splash.png`, `splash_dark.png` +- **Format**: PNG or SVG files for optimal quality + +### Asset Generation + +- **Configuration**: `config/assets/capacitor-assets.config.json` +- **Schema validation**: `config/assets/schema.json` +- **Build-time generation**: Platform assets generated via `capacitor-assets` +- **No VCS commits**: Generated assets are never committed to version control + +### Development Commands + +```bash +# Generate/update asset configurations +npm run assets:config + +# Validate asset configurations +npm run assets:validate + +# Clean generated platform assets (local dev only) +npm run assets:clean + +# Build with asset generation +npm run build:native +``` + +### Platform Support + +- **Android**: Adaptive icons with foreground/background, monochrome support +- **iOS**: LaunchScreen storyboard preferred, splash assets when needed +- **Web**: PWA icons generated during build to `dist/` (not committed) + +### Font Awesome Icons + +To add a Font Awesome icon, add to `fontawesome.ts` and reference with +`font-awesome` element and `icon` attribute with the hyphenated name. ## Other diff --git a/android/app/src/main/assets/capacitor.config.json b/android/app/src/main/assets/capacitor.config.json index 594ebca3..04ab8d0c 100644 --- a/android/app/src/main/assets/capacitor.config.json +++ b/android/app/src/main/assets/capacitor.config.json @@ -29,7 +29,7 @@ "splashFullScreen": true, "splashImmersive": true }, - "CapacitorSQLite": { + "CapSQLite": { "iosDatabaseLocation": "Library/CapacitorDatabase", "iosIsEncryption": false, "iosBiometric": { diff --git a/assets/README.md b/assets/README.md deleted file mode 100644 index b9272ff0..00000000 --- a/assets/README.md +++ /dev/null @@ -1,2 +0,0 @@ - -Application icons are here. They are processed for android & ios by the `capacitor-assets` command, as indicated in the BUILDING.md file. diff --git a/capacitor-assets.config.json b/capacitor-assets.config.json index d56533f4..92bd0414 100644 --- a/capacitor-assets.config.json +++ b/capacitor-assets.config.json @@ -1,36 +1,32 @@ { "icon": { - "ios": { - "source": "resources/ios/icon/icon.png", - "target": "ios/App/App/Assets.xcassets/AppIcon.appiconset" - }, "android": { - "source": "resources/android/icon/icon.png", + "adaptive": { + "background": "#121212", + "foreground": "resources/icon.png", + "monochrome": "resources/icon.png" + }, "target": "android/app/src/main/res" }, + "ios": { + "padding": 0, + "target": "ios/App/App/Assets.xcassets/AppIcon.appiconset" + }, + "source": "resources/icon.png", "web": { - "source": "resources/web/icon/icon.png", "target": "public/img/icons" } }, "splash": { - "ios": { - "source": "resources/ios/splash/splash.png", - "target": "ios/App/App/Assets.xcassets/Splash.imageset" - }, "android": { - "source": "resources/android/splash/splash.png", + "scale": "cover", "target": "android/app/src/main/res" - } - }, - "splashDark": { + }, + "darkSource": "resources/splash_dark.png", "ios": { - "source": "resources/ios/splash/splash_dark.png", - "target": "ios/App/App/Assets.xcassets/SplashDark.imageset" + "target": "ios/App/App/Assets.xcassets", + "useStoryBoard": true }, - "android": { - "source": "resources/android/splash/splash_dark.png", - "target": "android/app/src/main/res" - } + "source": "resources/splash.png" } -} \ No newline at end of file +} diff --git a/capacitor.config.ts b/capacitor.config.ts new file mode 100644 index 00000000..24ef38c6 --- /dev/null +++ b/capacitor.config.ts @@ -0,0 +1,116 @@ +import { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'app.timesafari', + appName: 'TimeSafari', + webDir: 'dist', + server: { + cleartext: true + }, + plugins: { + App: { + appUrlOpen: { + handlers: [ + { + url: 'timesafari://*', + autoVerify: true + } + ] + } + }, + SplashScreen: { + launchShowDuration: 3000, + launchAutoHide: true, + backgroundColor: '#ffffff', + androidSplashResourceName: 'splash', + androidScaleType: 'CENTER_CROP', + showSpinner: false, + androidSpinnerStyle: 'large', + iosSpinnerStyle: 'small', + spinnerColor: '#999999', + splashFullScreen: true, + splashImmersive: true + }, + CapSQLite: { + iosDatabaseLocation: 'Library/CapacitorDatabase', + iosIsEncryption: false, + iosBiometric: { + biometricAuth: false, + biometricTitle: 'Biometric login for TimeSafari' + }, + androidIsEncryption: false, + androidBiometric: { + biometricAuth: false, + biometricTitle: 'Biometric login for TimeSafari' + }, + electronIsEncryption: false + } + }, + ios: { + contentInset: 'never', + allowsLinkPreview: true, + scrollEnabled: true, + limitsNavigationsToAppBoundDomains: true, + backgroundColor: '#ffffff', + allowNavigation: [ + '*.timesafari.app', + '*.jsdelivr.net', + 'api.endorser.ch' + ] + }, + android: { + allowMixedContent: true, + captureInput: true, + webContentsDebuggingEnabled: false, + allowNavigation: [ + '*.timesafari.app', + '*.jsdelivr.net', + 'api.endorser.ch', + '10.0.2.2:3000' + ] + }, + electron: { + deepLinking: { + schemes: ['timesafari'] + }, + buildOptions: { + appId: 'app.timesafari', + productName: 'TimeSafari', + directories: { + output: 'dist-electron-packages' + }, + files: [ + 'dist/**/*', + 'electron/**/*' + ], + mac: { + category: 'public.app-category.productivity', + target: [ + { + target: 'dmg', + arch: ['x64', 'arm64'] + } + ] + }, + win: { + target: [ + { + target: 'nsis', + arch: ['x64'] + } + ] + }, + linux: { + target: [ + { + target: 'AppImage', + arch: ['x64'] + } + ], + category: 'Utility' + } + } + } +}; + +export default config; diff --git a/config/assets/capacitor-assets.config.json b/config/assets/capacitor-assets.config.json new file mode 100644 index 00000000..eb12403e --- /dev/null +++ b/config/assets/capacitor-assets.config.json @@ -0,0 +1,32 @@ +{ + "icon": { + "source": "resources/icon.png", + "android": { + "adaptive": { + "foreground": "resources/icon.png", + "background": "#121212", + "monochrome": "resources/icon.png" + }, + "target": "android/app/src/main/res" + }, + "ios": { + "padding": 0, + "target": "ios/App/App/Assets.xcassets/AppIcon.appiconset" + }, + "web": { + "target": "public/img/icons" + } + }, + "splash": { + "source": "resources/splash.png", + "darkSource": "resources/splash_dark.png", + "android": { + "scale": "cover", + "target": "android/app/src/main/res" + }, + "ios": { + "useStoryBoard": true, + "target": "ios/App/App/Assets.xcassets" + } + } +} diff --git a/config/assets/schema.json b/config/assets/schema.json new file mode 100644 index 00000000..89e76c0c --- /dev/null +++ b/config/assets/schema.json @@ -0,0 +1,119 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Capacitor Assets Configuration Schema", + "description": "Schema for validating capacitor-assets configuration files", + "type": "object", + "properties": { + "icon": { + "type": "object", + "properties": { + "source": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Source icon file path relative to project root" + }, + "android": { + "type": "object", + "properties": { + "adaptive": { + "type": "object", + "properties": { + "foreground": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Foreground icon for Android adaptive icons" + }, + "background": { + "type": ["string", "object"], + "description": "Background color or image for adaptive icons" + }, + "monochrome": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Monochrome icon for Android 13+" + } + }, + "required": ["foreground", "background"] + }, + "target": { + "type": "string", + "description": "Android target directory for generated icons" + } + } + }, + "ios": { + "type": "object", + "properties": { + "padding": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Padding ratio for iOS icons" + }, + "target": { + "type": "string", + "description": "iOS target directory for generated icons" + } + } + }, + "web": { + "type": "object", + "properties": { + "target": { + "type": "string", + "description": "Web target directory for generated icons" + } + } + } + }, + "required": ["source"], + "additionalProperties": false + }, + "splash": { + "type": "object", + "properties": { + "source": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Source splash screen file" + }, + "darkSource": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Dark mode splash screen file" + }, + "android": { + "type": "object", + "properties": { + "scale": { + "type": "string", + "enum": ["cover", "contain", "fill"], + "description": "Android splash screen scaling mode" + }, + "target": { + "type": "string", + "description": "Android target directory for splash screens" + } + } + }, + "ios": { + "type": "object", + "properties": { + "useStoryBoard": { + "type": "boolean", + "description": "Use LaunchScreen storyboard instead of splash assets" + }, + "target": { + "type": "string", + "description": "iOS target directory for splash screens" + } + } + } + }, + "required": ["source"], + "additionalProperties": false + } + }, + "required": ["icon", "splash"], + "additionalProperties": false +} diff --git a/doc/asset-migration-plan.md b/doc/asset-migration-plan.md new file mode 100644 index 00000000..3a05353c --- /dev/null +++ b/doc/asset-migration-plan.md @@ -0,0 +1,214 @@ +# TimeSafari Asset Configuration Migration Plan + +**Author**: Matthew Raymer +**Date**: 2025-08-14 +**Status**: 🎯 **IMPLEMENTATION** - Ready for Execution + +## Overview + +This document outlines the migration from the current mixed asset management +system to a standardized, single-source asset configuration approach using +`capacitor-assets` as the standard generator. + +## Current State Analysis + +### Asset Sources (Duplicated) + +- **`assets/` directory**: Contains `icon.png`, `splash.png`, `splash_dark.png` +- **`resources/` directory**: Contains identical files in platform-specific subdirectories +- **Result**: Duplicate storage, confusion about source of truth + +### Asset Generation (Manual) + +- **Custom scripts**: `generate-icons.sh`, `generate-ios-assets.sh`, `generate-android-icons.sh` +- **Bypass capacitor-assets**: Manual ImageMagick-based generation +- **Inconsistent outputs**: Different generation methods for each platform + +### Configuration (Scattered) + +- **`capacitor-assets.config.json`**: Basic configuration at root +- **Platform-specific configs**: Mixed in various build scripts +- **No validation**: No schema or consistency checks + +## Target State + +### Single Source of Truth + +- **`resources/` directory**: Capacitor default location for source assets +- **Eliminate duplication**: Remove `assets/` directory after migration +- **Standardized paths**: All tools read from `resources/` + +### Standardized Generation + +- **`capacitor-assets`**: Single tool for all platform asset generation +- **Build-time generation**: Assets generated during build, not committed +- **Deterministic outputs**: Same inputs → same outputs every time + +### Centralized Configuration + +- **`config/assets/`**: All asset-related configuration files +- **Schema validation**: JSON schema for configuration validation +- **CI safeguards**: Automated validation and compliance checks + +## Migration Steps + +### Phase 1: Foundation Setup ✅ + +- [x] Create `config/assets/` directory structure +- [x] Create asset configuration schema (`schema.json`) +- [x] Create enhanced capacitor-assets configuration +- [x] Convert `capacitor.config.json` to `capacitor.config.ts` +- [x] Pin Node.js version (`.nvmrc`, `.node-version`) +- [x] Create dev-time asset configuration generator +- [x] Create asset configuration validator +- [x] Add npm scripts for asset management +- [x] Update `.gitignore` with proper asset exclusions +- [x] Create CI workflow for asset validation + +### Phase 2: Validation & Testing + +- [ ] Run `npm run assets:config` to generate new configuration +- [ ] Run `npm run assets:validate` to verify configuration +- [ ] Test `npm run build:native` workflow +- [ ] Verify CI workflow passes all checks +- [ ] Confirm no platform assets are committed to VCS + +### Phase 3: Cleanup & Removal + +- [ ] Remove `assets/` directory (after validation) +- [ ] Remove manual asset generation scripts +- [ ] Remove asset checking scripts +- [ ] Update documentation references +- [ ] Final validation of clean state + +## Implementation Details + +### File Structure + +``` +resources/ # Image sources ONLY + icon.png + splash.png + splash_dark.png + +config/assets/ # Versioned config & schema + capacitor-assets.config.json + schema.json + +scripts/ + assets-config.js # Dev-time config generator + assets-validator.js # Schema validator +``` + +### Configuration Schema + +The schema enforces: +- Source files must be in `resources/` directory +- Required fields for icon and splash sections +- Android adaptive icon support (foreground/background/monochrome) +- iOS LaunchScreen preferences +- Target directory validation + +### CI Safeguards + +- **Schema validation**: Configuration must comply with schema +- **Source file validation**: All referenced files must exist +- **Platform asset denial**: Reject commits with generated assets +- **Clean tree enforcement**: Build must not modify committed configs + +## Testing Strategy + +### Local Validation + +```bash +# Generate configuration +npm run assets:config + +# Validate configuration +npm run assets:validate + +# Test build workflow +npm run build:native + +# Clean generated assets +npm run assets:clean +``` + +### CI Validation + +- **Asset validation workflow**: Runs on asset-related changes +- **Schema compliance**: Ensures configuration follows schema +- **Source file existence**: Verifies all referenced files exist +- **Platform asset detection**: Prevents committed generated assets +- **Build tree verification**: Ensures clean tree after build + +## Risk Mitigation + +### Data Loss Prevention + +- **Backup branch**: Create backup before removing `assets/` +- **Validation checks**: Multiple validation steps before removal +- **Gradual migration**: Phase-by-phase approach with rollback capability + +### Build Continuity + +- **Per-platform scripts unchanged**: All existing build orchestration preserved +- **Standard toolchain**: Uses capacitor-assets, not custom scripts +- **Fallback support**: Manual scripts remain until migration complete + +### Configuration Consistency + +- **Schema enforcement**: JSON schema prevents invalid configurations +- **CI validation**: Automated checks catch configuration issues +- **Documentation updates**: Clear guidance for future changes + +## Success Criteria + +### Technical Requirements + +- [ ] Single source of truth in `resources/` directory +- [ ] All platform assets generated via `capacitor-assets` +- [ ] No manual asset generation scripts +- [ ] Configuration validation passes all checks +- [ ] CI workflow enforces asset policies + +### Quality Metrics + +- [ ] Zero duplicate asset sources +- [ ] 100% configuration schema compliance +- [ ] No platform assets committed to VCS +- [ ] Clean build tree after asset generation +- [ ] Deterministic asset outputs + +### User Experience + +- [ ] Clear asset management documentation +- [ ] Simple development commands +- [ ] Consistent asset generation across platforms +- [ ] Reduced confusion about asset sources + +## Next Steps + +1. **Execute Phase 2**: Run validation and testing steps +2. **Verify CI workflow**: Ensure all checks pass +3. **Execute Phase 3**: Remove duplicate assets and scripts +4. **Update documentation**: Finalize README and BUILDING.md +5. **Team training**: Ensure all developers understand new workflow + +## Rollback Plan + +If issues arise during migration: + +1. **Restore backup branch**: `git checkout backup-before-asset-migration` +2. **Revert configuration changes**: Remove new config files +3. **Restore manual scripts**: Re-enable previous asset generation +4. **Investigate issues**: Identify and resolve root causes +5. **Plan revised migration**: Adjust approach based on lessons learned + +--- + +**Status**: Ready for Phase 2 execution +**Priority**: High +**Estimated Effort**: 2-3 hours +**Dependencies**: CI workflow validation +**Stakeholders**: Development team diff --git a/package.json b/package.json index 7b27258d..930cc8cf 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,10 @@ "auto-run:electron": "./scripts/auto-run.sh --platform=electron", "build:capacitor": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --mode capacitor --config vite.config.capacitor.mts", "build:capacitor:sync": "npm run build:capacitor && npx cap sync", + "build:native": "vite build && npx cap sync && npx capacitor-assets generate", + "assets:config": "node scripts/assets-config.js", + "assets:validate": "node scripts/assets-validator.js", + "assets:clean": "rimraf android/app/src/main/res/mipmap-* ios/App/App/Assets.xcassets/**/AppIcon*.png ios/App/App/Assets.xcassets/**/Splash*.png || true", "build:ios": "./scripts/build-ios.sh", "build:ios:dev": "./scripts/build-ios.sh --dev", "build:ios:test": "./scripts/build-ios.sh --test", diff --git a/assets/icon.png b/resources/icon.png similarity index 100% rename from assets/icon.png rename to resources/icon.png diff --git a/assets/splash.png b/resources/splash.png similarity index 100% rename from assets/splash.png rename to resources/splash.png diff --git a/assets/splash_dark.png b/resources/splash_dark.png similarity index 100% rename from assets/splash_dark.png rename to resources/splash_dark.png diff --git a/scripts/assets-config.js b/scripts/assets-config.js new file mode 100644 index 00000000..926ff1fa --- /dev/null +++ b/scripts/assets-config.js @@ -0,0 +1,174 @@ +#!/usr/bin/env node + +/** + * TimeSafari Asset Configuration Generator + * Generates capacitor-assets configuration files with deterministic outputs + * Author: Matthew Raymer + * + * Usage: node scripts/assets-config.js + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const PROJECT_ROOT = path.dirname(__dirname); + +/** + * Generate deterministic capacitor-assets configuration + * @returns {Object} Sorted, stable configuration object + */ +function generateAssetConfig() { + const config = { + icon: { + source: "resources/icon.png", + android: { + adaptive: { + foreground: "resources/icon.png", + background: "#121212", + monochrome: "resources/icon.png" + }, + target: "android/app/src/main/res" + }, + ios: { + padding: 0, + target: "ios/App/App/Assets.xcassets/AppIcon.appiconset" + }, + web: { + target: "public/img/icons" + } + }, + splash: { + source: "resources/splash.png", + darkSource: "resources/splash_dark.png", + android: { + scale: "cover", + target: "android/app/src/main/res" + }, + ios: { + useStoryBoard: true, + target: "ios/App/App/Assets.xcassets" + } + } + }; + + return sortObjectKeys(config); +} + +/** + * Sort object keys recursively for deterministic output + * @param {Object} obj - Object to sort + * @returns {Object} Object with sorted keys + */ +function sortObjectKeys(obj) { + if (obj === null || typeof obj !== 'object') { + return obj; + } + + if (Array.isArray(obj)) { + return obj.map(sortObjectKeys); + } + + const sorted = {}; + Object.keys(obj) + .sort() + .forEach(key => { + sorted[key] = sortObjectKeys(obj[key]); + }); + + return sorted; +} + +/** + * Validate that required source files exist + */ +function validateSourceFiles() { + const requiredFiles = [ + 'resources/icon.png', + 'resources/splash.png', + 'resources/splash_dark.png' + ]; + + const missingFiles = requiredFiles.filter(file => { + const filePath = path.join(PROJECT_ROOT, file); + return !fs.existsSync(filePath); + }); + + if (missingFiles.length > 0) { + console.error('❌ Missing required source files:'); + missingFiles.forEach(file => console.error(` ${file}`)); + process.exit(1); + } + + console.log('✅ All required source files found'); +} + +/** + * Write configuration to file with consistent formatting + * @param {Object} config - Configuration object + * @param {string} outputPath - Output file path + */ +function writeConfig(config, outputPath) { + const jsonString = JSON.stringify(config, null, 2); + + // Ensure consistent line endings and no trailing whitespace + const cleanJson = jsonString + .split('\n') + .map(line => line.trimEnd()) + .join('\n') + '\n'; + + fs.writeFileSync(outputPath, cleanJson, 'utf8'); + console.log(`✅ Configuration written to: ${outputPath}`); +} + +/** + * Main execution function + */ +function main() { + console.log('🔄 Generating TimeSafari asset configuration...'); + console.log(`📁 Project root: ${PROJECT_ROOT}`); + console.log(`📅 Generated: ${new Date().toISOString()}`); + + try { + // Validate source files exist + validateSourceFiles(); + + // Generate configuration + const config = generateAssetConfig(); + + // Ensure config directory exists + const configDir = path.join(PROJECT_ROOT, 'config', 'assets'); + if (!fs.existsSync(configDir)) { + fs.mkdirSync(configDir, { recursive: true }); + } + + // Write configuration files + const capacitorAssetsConfigPath = path.join(configDir, 'capacitor-assets.config.json'); + writeConfig(config, capacitorAssetsConfigPath); + + // Copy to root for capacitor-assets discovery + const rootConfigPath = path.join(PROJECT_ROOT, 'capacitor-assets.config.json'); + writeConfig(config, rootConfigPath); + + console.log('🎉 Asset configuration generation completed successfully!'); + console.log(''); + console.log('📋 Next steps:'); + console.log(' 1. Review the generated configuration'); + console.log(' 2. Commit the configuration files'); + console.log(' 3. Run "npm run assets:validate" to verify'); + console.log(' 4. Use "npm run build:native" for builds'); + + } catch (error) { + console.error('❌ Configuration generation failed:', error.message); + process.exit(1); + } +} + +// Run if called directly +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +} + +export { generateAssetConfig, sortObjectKeys, validateSourceFiles }; diff --git a/scripts/assets-validator.js b/scripts/assets-validator.js new file mode 100644 index 00000000..fcdbcdff --- /dev/null +++ b/scripts/assets-validator.js @@ -0,0 +1,218 @@ +#!/usr/bin/env node + +/** + * TimeSafari Asset Configuration Validator + * Validates capacitor-assets configuration against schema and source files + * Author: Matthew Raymer + * + * Usage: node scripts/assets-validator.js [config-path] + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const PROJECT_ROOT = path.dirname(__dirname); + +/** + * Load and parse JSON file + * @param {string} filePath - Path to JSON file + * @returns {Object} Parsed JSON object + */ +function loadJsonFile(filePath) { + try { + const content = fs.readFileSync(filePath, 'utf8'); + return JSON.parse(content); + } catch (error) { + throw new Error(`Failed to load ${filePath}: ${error.message}`); + } +} + +/** + * Validate configuration against schema + * @param {Object} config - Configuration object to validate + * @param {Object} schema - JSON schema for validation + * @returns {Array} Array of validation errors + */ +function validateAgainstSchema(config, schema) { + const errors = []; + + // Basic structure validation + if (!config.icon || !config.splash) { + errors.push('Configuration must contain both "icon" and "splash" sections'); + } + + // Icon validation + if (config.icon) { + if (!config.icon.source) { + errors.push('Icon section must contain "source" field'); + } else if (!/^resources\/.*\.(png|svg)$/.test(config.icon.source)) { + errors.push('Icon source must be a PNG or SVG file in resources/ directory'); + } + + // Android adaptive icon validation + if (config.icon.android?.adaptive) { + const adaptive = config.icon.android.adaptive; + if (!adaptive.foreground || !adaptive.background) { + errors.push('Android adaptive icon must have both foreground and background'); + } + if (adaptive.foreground && !/^resources\/.*\.(png|svg)$/.test(adaptive.foreground)) { + errors.push('Android adaptive foreground must be a PNG or SVG file in resources/ directory'); + } + } + } + + // Splash validation + if (config.splash) { + if (!config.splash.source) { + errors.push('Splash section must contain "source" field'); + } else if (!/^resources\/.*\.(png|svg)$/.test(config.splash.source)) { + errors.push('Splash source must be a PNG or SVG file in resources/ directory'); + } + + if (config.splash.darkSource && !/^resources\/.*\.(png|svg)$/.test(config.splash.darkSource)) { + errors.push('Dark splash source must be a PNG or SVG file in resources/ directory'); + } + } + + return errors; +} + +/** + * Validate that source files exist + * @param {Object} config - Configuration object + * @returns {Array} Array of missing file errors + */ +function validateSourceFiles(config) { + const errors = []; + const requiredFiles = new Set(); + + // Collect all required source files + if (config.icon?.source) requiredFiles.add(config.icon.source); + if (config.icon?.android?.adaptive?.foreground) requiredFiles.add(config.icon.android.adaptive.foreground); + if (config.icon?.android?.adaptive?.monochrome) requiredFiles.add(config.icon.android.adaptive.monochrome); + if (config.splash?.source) requiredFiles.add(config.splash.source); + if (config.splash?.darkSource) requiredFiles.add(config.splash.darkSource); + + // Check each file exists + requiredFiles.forEach(file => { + const filePath = path.join(PROJECT_ROOT, file); + if (!fs.existsSync(filePath)) { + errors.push(`Source file not found: ${file}`); + } + }); + + return errors; +} + +/** + * Validate target directories are writable + * @param {Object} config - Configuration object + * @returns {Array} Array of directory validation errors + */ +function validateTargetDirectories(config) { + const errors = []; + const targetDirs = new Set(); + + // Collect all target directories + if (config.icon?.android?.target) targetDirs.add(config.icon.android.target); + if (config.icon?.ios?.target) targetDirs.add(config.icon.ios.target); + if (config.icon?.web?.target) targetDirs.add(config.icon.web.target); + if (config.splash?.android?.target) targetDirs.add(config.splash.android.target); + if (config.splash?.ios?.target) targetDirs.add(config.splash.ios.target); + + // Check each target directory + targetDirs.forEach(dir => { + const dirPath = path.join(PROJECT_ROOT, dir); + const parentDir = path.dirname(dirPath); + + if (!fs.existsSync(parentDir)) { + errors.push(`Parent directory does not exist: ${parentDir}`); + } else if (!fs.statSync(parentDir).isDirectory()) { + errors.push(`Parent path is not a directory: ${parentDir}`); + } + }); + + return errors; +} + +/** + * Main validation function + * @param {string} configPath - Path to configuration file + * @returns {boolean} True if validation passes + */ +function validateConfiguration(configPath) { + console.log('🔍 Validating TimeSafari asset configuration...'); + console.log(`📁 Config file: ${configPath}`); + console.log(`📁 Project root: ${PROJECT_ROOT}`); + + try { + // Load configuration + const config = loadJsonFile(configPath); + console.log('✅ Configuration file loaded successfully'); + + // Load schema + const schemaPath = path.join(PROJECT_ROOT, 'config', 'assets', 'schema.json'); + const schema = loadJsonFile(schemaPath); + console.log('✅ Schema file loaded successfully'); + + // Perform validations + const schemaErrors = validateAgainstSchema(config, schema); + const fileErrors = validateSourceFiles(config); + const dirErrors = validateTargetDirectories(config); + + // Report results + const allErrors = [...schemaErrors, ...fileErrors, ...dirErrors]; + + if (allErrors.length === 0) { + console.log('🎉 All validations passed successfully!'); + console.log(''); + console.log('📋 Configuration summary:'); + console.log(` Icon source: ${config.icon?.source || 'NOT SET'}`); + console.log(` Splash source: ${config.splash?.source || 'NOT SET'}`); + console.log(` Dark splash: ${config.splash?.darkSource || 'NOT SET'}`); + console.log(` Android adaptive: ${config.icon?.android?.adaptive ? 'ENABLED' : 'DISABLED'}`); + console.log(` iOS LaunchScreen: ${config.splash?.ios?.useStoryBoard ? 'ENABLED' : 'DISABLED'}`); + return true; + } else { + console.error('❌ Validation failed with the following errors:'); + allErrors.forEach((error, index) => { + console.error(` ${index + 1}. ${error}`); + }); + return false; + } + + } catch (error) { + console.error('❌ Validation failed:', error.message); + return false; + } +} + +/** + * Main execution function + */ +function main() { + const configPath = process.argv[2] || path.join(PROJECT_ROOT, 'capacitor-assets.config.json'); + + if (!fs.existsSync(configPath)) { + console.error(`❌ Configuration file not found: ${configPath}`); + console.log(''); + console.log('💡 Available options:'); + console.log(' - Use default: capacitor-assets.config.json'); + console.log(' - Specify path: node scripts/assets-validator.js path/to/config.json'); + console.log(' - Generate config: npm run assets:config'); + process.exit(1); + } + + const success = validateConfiguration(configPath); + process.exit(success ? 0 : 1); +} + +// Run if called directly +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +} + +export { validateConfiguration, validateAgainstSchema, validateSourceFiles, validateTargetDirectories }; diff --git a/scripts/check-android-resources.sh b/scripts/check-android-resources.sh deleted file mode 100755 index cd994088..00000000 --- a/scripts/check-android-resources.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/bin/bash - -# TimeSafari Android Resource Check Script -# Checks for missing Android resources and automatically fixes common issues -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -ANDROID_RES_DIR="$PROJECT_ROOT/android/app/src/main/res" -ASSETS_DIR="$PROJECT_ROOT/assets" - -echo "=== TimeSafari Android Resource Check ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Checking Android resources" - -# Function to check if a file exists -check_file() { - local file="$1" - local description="$2" - if [ -f "$file" ]; then - echo "[✓] $description: $file" - return 0 - else - echo "[✗] $description: $file (MISSING)" - return 1 - fi -} - -# Function to check if a directory exists and has files -check_directory() { - local dir="$1" - local description="$2" - if [ -d "$dir" ] && [ "$(ls -A "$dir" 2>/dev/null)" ]; then - echo "[✓] $description: $dir" - return 0 - else - echo "[✗] $description: $dir (MISSING OR EMPTY)" - return 1 - fi -} - -# Track issues -issues_found=0 -fixes_applied=0 - -echo "[INFO] Checking splash screen resources..." -# Ensure drawable directory exists -if [ ! -d "$ANDROID_RES_DIR/drawable" ]; then - echo "[FIX] Creating drawable directory..." - mkdir -p "$ANDROID_RES_DIR/drawable" - fixes_applied=$((fixes_applied + 1)) -fi - -# Check splash screen resources -if ! check_file "$ANDROID_RES_DIR/drawable/splash.png" "Splash screen (light)"; then - if [ -f "$ASSETS_DIR/splash.png" ]; then - echo "[FIX] Copying splash.png to Android resources..." - cp "$ASSETS_DIR/splash.png" "$ANDROID_RES_DIR/drawable/splash.png" - fixes_applied=$((fixes_applied + 1)) - else - issues_found=$((issues_found + 1)) - fi -fi - -if ! check_file "$ANDROID_RES_DIR/drawable/splash_dark.png" "Splash screen (dark)"; then - if [ -f "$ASSETS_DIR/splash_dark.png" ]; then - echo "[FIX] Copying splash_dark.png to Android resources..." - cp "$ASSETS_DIR/splash_dark.png" "$ANDROID_RES_DIR/drawable/splash_dark.png" - fixes_applied=$((fixes_applied + 1)) - else - issues_found=$((issues_found + 1)) - fi -fi - -echo "[INFO] Checking launcher icon resources..." -# Ensure mipmap directories exist -mipmap_dirs=("mdpi" "hdpi" "xhdpi" "xxhdpi" "xxxhdpi" "anydpi-v26") -for dir in "${mipmap_dirs[@]}"; do - if [ ! -d "$ANDROID_RES_DIR/mipmap-$dir" ]; then - echo "[FIX] Creating mipmap-$dir directory..." - mkdir -p "$ANDROID_RES_DIR/mipmap-$dir" - fixes_applied=$((fixes_applied + 1)) - fi -done - -# Check launcher icon resources -required_icons=( - "mipmap-mdpi/ic_launcher.png" - "mipmap-hdpi/ic_launcher.png" - "mipmap-xhdpi/ic_launcher.png" - "mipmap-xxhdpi/ic_launcher.png" - "mipmap-xxxhdpi/ic_launcher.png" - "mipmap-anydpi-v26/ic_launcher.xml" - "mipmap-anydpi-v26/ic_launcher_round.xml" -) - -missing_icons=0 -for icon in "${required_icons[@]}"; do - if ! check_file "$ANDROID_RES_DIR/$icon" "Launcher icon: $icon"; then - missing_icons=$((missing_icons + 1)) - fi -done - -if [ $missing_icons -gt 0 ]; then - echo "[FIX] Missing launcher icons detected. Running icon generation script..." - if [ -f "$SCRIPT_DIR/generate-android-icons.sh" ]; then - "$SCRIPT_DIR/generate-android-icons.sh" - fixes_applied=$((fixes_applied + 1)) - else - echo "[ERROR] Icon generation script not found: $SCRIPT_DIR/generate-android-icons.sh" - issues_found=$((issues_found + 1)) - fi -fi - -echo "[INFO] Checking Capacitor platform status..." -# Check if Android platform is properly initialized -if [ ! -d "$PROJECT_ROOT/android" ]; then - echo "[ERROR] Android platform directory not found" - issues_found=$((issues_found + 1)) -elif [ ! -f "$PROJECT_ROOT/android/app/src/main/AndroidManifest.xml" ]; then - echo "[ERROR] AndroidManifest.xml not found - platform may be corrupted" - issues_found=$((issues_found + 1)) -else - echo "[✓] Android platform appears to be properly initialized" -fi - -# Check for common build issues -echo "[INFO] Checking for common build issues..." - -# Check for invalid resource names (dashes in filenames) -invalid_resources=$(find "$ANDROID_RES_DIR" -name "*-*" -type f 2>/dev/null | grep -E '\.(png|jpg|jpeg|gif|xml)$' || true) -if [ -n "$invalid_resources" ]; then - echo "[WARNING] Found resources with invalid names (containing dashes):" - echo "$invalid_resources" | while read -r file; do - echo " - $file" - done - echo "[INFO] Android resource names must contain only lowercase a-z, 0-9, or underscore" - issues_found=$((issues_found + 1)) -fi - -# Summary -echo "" -echo "=== Resource Check Summary ===" -if [ $issues_found -eq 0 ] && [ $fixes_applied -eq 0 ]; then - echo "[SUCCESS] All Android resources are present and valid" - exit 0 -elif [ $fixes_applied -gt 0 ]; then - echo "[SUCCESS] Fixed $fixes_applied resource issues automatically" - if [ $issues_found -gt 0 ]; then - echo "[WARNING] $issues_found issues remain that require manual attention" - exit 1 - else - exit 0 - fi -else - echo "[ERROR] Found $issues_found resource issues that require manual attention" - exit 1 -fi \ No newline at end of file diff --git a/scripts/check-ios-resources.sh b/scripts/check-ios-resources.sh deleted file mode 100755 index 70466fb2..00000000 --- a/scripts/check-ios-resources.sh +++ /dev/null @@ -1,294 +0,0 @@ -#!/bin/bash - -# TimeSafari iOS Resource Check Script -# Checks for missing iOS resources and automatically fixes common issues -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -IOS_ASSETS_DIR="$PROJECT_ROOT/ios/App/App/Assets.xcassets" -RESOURCES_DIR="$PROJECT_ROOT/resources/ios" - -echo "=== TimeSafari iOS Resource Check ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Checking iOS resources" - -# Function to check if a file exists -check_file() { - local file="$1" - local description="$2" - if [ -f "$file" ]; then - echo "[✓] $description: $file" - return 0 - else - echo "[✗] $description: $file (MISSING)" - return 1 - fi -} - -# Function to check if a directory exists and has files -check_directory() { - local dir="$1" - local description="$2" - if [ -d "$dir" ] && [ "$(ls -A "$dir" 2>/dev/null)" ]; then - echo "[✓] $description: $dir" - return 0 - else - echo "[✗] $description: $dir (MISSING OR EMPTY)" - return 1 - fi -} - -# Track issues -issues_found=0 -fixes_applied=0 - -echo "[INFO] Checking iOS asset catalog structure..." -# Check if Assets.xcassets directory exists -if ! check_directory "$IOS_ASSETS_DIR" "iOS Assets.xcassets directory"; then - echo "[FIX] Creating iOS Assets.xcassets directory..." - mkdir -p "$IOS_ASSETS_DIR" - fixes_applied=$((fixes_applied + 1)) -fi - -# Check main Contents.json -if ! check_file "$IOS_ASSETS_DIR/Contents.json" "Main Assets.xcassets Contents.json"; then - echo "[FIX] Creating main Assets.xcassets Contents.json..." - cat > "$IOS_ASSETS_DIR/Contents.json" << 'EOF' -{ - "info" : { - "version" : 1, - "author" : "xcode" - } -} -EOF - fixes_applied=$((fixes_applied + 1)) -fi - -echo "[INFO] Checking App Icon resources..." -# Check App Icon directory -if ! check_directory "$IOS_ASSETS_DIR/AppIcon.appiconset" "App Icon directory"; then - echo "[FIX] Creating App Icon directory..." - mkdir -p "$IOS_ASSETS_DIR/AppIcon.appiconset" - fixes_applied=$((fixes_applied + 1)) -fi - -# Check App Icon Contents.json -if ! check_file "$IOS_ASSETS_DIR/AppIcon.appiconset/Contents.json" "App Icon Contents.json"; then - echo "[FIX] Creating App Icon Contents.json..." - cat > "$IOS_ASSETS_DIR/AppIcon.appiconset/Contents.json" << 'EOF' -{ - "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} -EOF - fixes_applied=$((fixes_applied + 1)) -fi - -echo "[INFO] Checking Splash Screen resources..." -# Check Splash directory -if ! check_directory "$IOS_ASSETS_DIR/Splash.imageset" "Splash screen directory"; then - echo "[FIX] Creating Splash screen directory..." - mkdir -p "$IOS_ASSETS_DIR/Splash.imageset" - fixes_applied=$((fixes_applied + 1)) -fi - -# Check Splash Contents.json -if ! check_file "$IOS_ASSETS_DIR/Splash.imageset/Contents.json" "Splash screen Contents.json"; then - echo "[FIX] Creating Splash screen Contents.json..." - cat > "$IOS_ASSETS_DIR/Splash.imageset/Contents.json" << 'EOF' -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} -EOF - fixes_applied=$((fixes_applied + 1)) -fi - -# Check SplashDark directory -if ! check_directory "$IOS_ASSETS_DIR/SplashDark.imageset" "Dark splash screen directory"; then - echo "[FIX] Creating Dark splash screen directory..." - mkdir -p "$IOS_ASSETS_DIR/SplashDark.imageset" - fixes_applied=$((fixes_applied + 1)) -fi - -# Check SplashDark Contents.json -if ! check_file "$IOS_ASSETS_DIR/SplashDark.imageset/Contents.json" "Dark splash screen Contents.json"; then - echo "[FIX] Creating Dark splash screen Contents.json..." - cat > "$IOS_ASSETS_DIR/SplashDark.imageset/Contents.json" << 'EOF' -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} -EOF - fixes_applied=$((fixes_applied + 1)) -fi - -echo "[INFO] Checking source resource files..." -# Check if source resources exist -if ! check_file "$RESOURCES_DIR/icon/icon.png" "iOS icon source"; then - issues_found=$((issues_found + 1)) -fi - -if ! check_file "$RESOURCES_DIR/splash/splash.png" "iOS splash source"; then - issues_found=$((issues_found + 1)) -fi - -if ! check_file "$RESOURCES_DIR/splash/splash_dark.png" "iOS dark splash source"; then - issues_found=$((issues_found + 1)) -fi - -echo "[INFO] Checking iOS platform status..." -# Check if iOS platform is properly initialized -if [ ! -d "$PROJECT_ROOT/ios" ]; then - echo "[ERROR] iOS platform directory not found" - issues_found=$((issues_found + 1)) -elif [ ! -f "$PROJECT_ROOT/ios/App/App/Info.plist" ]; then - echo "[ERROR] Info.plist not found - platform may be corrupted" - issues_found=$((issues_found + 1)) -else - echo "[✓] iOS platform appears to be properly initialized" -fi - -# Summary -echo "" -echo "=== iOS Resource Check Summary ===" -if [ $issues_found -eq 0 ] && [ $fixes_applied -eq 0 ]; then - echo "[SUCCESS] All iOS resources are present and valid" - exit 0 -elif [ $fixes_applied -gt 0 ]; then - echo "[SUCCESS] Fixed $fixes_applied resource issues automatically" - if [ $issues_found -gt 0 ]; then - echo "[WARNING] $issues_found issues remain that require manual attention" - echo "[NOTE] iOS builds require macOS with Xcode - cannot build on Linux" - exit 1 - else - exit 0 - fi -else - echo "[ERROR] Found $issues_found resource issues that require manual attention" - echo "[NOTE] iOS builds require macOS with Xcode - cannot build on Linux" - exit 1 -fi diff --git a/scripts/generate-android-icons.sh b/scripts/generate-android-icons.sh deleted file mode 100755 index aa2775ca..00000000 --- a/scripts/generate-android-icons.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/bin/bash - -# TimeSafari Android Icon Generation Script -# Generates all required Android launcher icon sizes from assets/icon.png -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -ASSETS_DIR="$PROJECT_ROOT/assets" -ANDROID_RES_DIR="$PROJECT_ROOT/android/app/src/main/res" - -echo "=== TimeSafari Android Icon Generation ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Starting Android icon generation" - -# Check if source icon exists -if [ ! -f "$ASSETS_DIR/icon.png" ]; then - echo "[ERROR] Source icon not found: $ASSETS_DIR/icon.png" - exit 1 -fi - -# Check if ImageMagick is available and determine the correct command -IMAGEMAGICK_CMD="" -if command -v magick &> /dev/null; then - IMAGEMAGICK_CMD="magick" - echo "[INFO] Using ImageMagick v7+ (magick command)" -elif command -v convert &> /dev/null; then - IMAGEMAGICK_CMD="convert" - echo "[INFO] Using ImageMagick v6 (convert command)" -else - echo "[ERROR] ImageMagick not found. Please install ImageMagick." - echo " Arch: sudo pacman -S imagemagick" - echo " Ubuntu: sudo apt-get install imagemagick" - echo " macOS: brew install imagemagick" - exit 1 -fi - -# Create mipmap directories if they don't exist -mkdir -p "$ANDROID_RES_DIR/mipmap-hdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-mdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xhdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xxhdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xxxhdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-anydpi-v26" - -echo "[INFO] Generating launcher icons..." - -# Function to resize image using the appropriate ImageMagick command -resize_image() { - local input="$1" - local output="$2" - local size="$3" - - if [ "$IMAGEMAGICK_CMD" = "magick" ]; then - # ImageMagick v7+ syntax - magick "$input" -resize "${size}x${size}" "$output" - else - # ImageMagick v6 syntax - convert "$input" -resize "${size}x${size}" "$output" - fi -} - -# Generate launcher icons for different densities -# Android launcher icon sizes: mdpi=48, hdpi=72, xhdpi=96, xxhdpi=144, xxxhdpi=192 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher.png" 48 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher.png" 72 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher.png" 96 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher.png" 144 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher.png" 192 - -# Generate round launcher icons -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher_round.png" 48 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher_round.png" 72 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher_round.png" 96 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher_round.png" 144 -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher_round.png" 192 - -# Create simple launcher XML files (no adaptive icons for now) -cat > "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher.xml" << 'EOF' - - - - - -EOF - -cat > "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher_round.xml" << 'EOF' - - - - - -EOF - -# Create foreground mipmap files for adaptive icons -resize_image "$ASSETS_DIR/icon.png" "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher_foreground.png" 108 - -echo "[SUCCESS] Generated Android launcher icons:" -echo " - mipmap-mdpi/ic_launcher.png (48x48)" -echo " - mipmap-hdpi/ic_launcher.png (72x72)" -echo " - mipmap-xhdpi/ic_launcher.png (96x96)" -echo " - mipmap-xxhdpi/ic_launcher.png (144x144)" -echo " - mipmap-xxxhdpi/ic_launcher.png (192x192)" -echo " - mipmap-anydpi-v26/ic_launcher_foreground.png (108x108)" -echo " - Updated mipmap-anydpi-v26 XML files" -echo "[SUCCESS] Android icon generation completed successfully!" \ No newline at end of file diff --git a/scripts/generate-icons.sh b/scripts/generate-icons.sh deleted file mode 100755 index 3f0062ed..00000000 --- a/scripts/generate-icons.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash - -# TimeSafari Android Icon Generation Script (Placeholder Icons) -# Generates placeholder Android launcher icons with "TS" text -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -ANDROID_RES_DIR="$PROJECT_ROOT/android/app/src/main/res" - -echo "=== TimeSafari Android Placeholder Icon Generation ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Starting Android placeholder icon generation" - -# Check if ImageMagick is available and determine the correct command -IMAGEMAGICK_CMD="" -if command -v magick &> /dev/null; then - IMAGEMAGICK_CMD="magick" - echo "[INFO] Using ImageMagick v7+ (magick command)" -elif command -v convert &> /dev/null; then - IMAGEMAGICK_CMD="convert" - echo "[INFO] Using ImageMagick v6 (convert command)" -else - echo "[ERROR] ImageMagick not found. Please install ImageMagick." - echo " Arch: sudo pacman -S imagemagick" - echo " Ubuntu: sudo apt-get install imagemagick" - echo " macOS: brew install imagemagick" - exit 1 -fi - -# Create directories if they don't exist -mkdir -p "$ANDROID_RES_DIR/mipmap-mdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-hdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xhdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xxhdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xxxhdpi" - -# Function to generate placeholder icon using the appropriate ImageMagick command -generate_placeholder_icon() { - local size="$1" - local output="$2" - local pointsize="$3" - - if [ "$IMAGEMAGICK_CMD" = "magick" ]; then - # ImageMagick v7+ syntax - magick -size "${size}x${size}" xc:blue -gravity center -pointsize "$pointsize" -fill white -annotate 0 "TS" "$output" - else - # ImageMagick v6 syntax - convert -size "${size}x${size}" xc:blue -gravity center -pointsize "$pointsize" -fill white -annotate 0 "TS" "$output" - fi -} - -echo "[INFO] Generating placeholder launcher icons..." - -# Generate placeholder icons using ImageMagick -generate_placeholder_icon 48 "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher.png" 20 -generate_placeholder_icon 72 "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher.png" 30 -generate_placeholder_icon 96 "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher.png" 40 -generate_placeholder_icon 144 "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher.png" 60 -generate_placeholder_icon 192 "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher.png" 80 - -echo "[INFO] Copying to round versions..." - -# Copy to round versions -cp "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher.png" "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher_round.png" -cp "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher.png" "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher_round.png" -cp "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher.png" "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher_round.png" -cp "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher.png" "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher_round.png" -cp "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher.png" "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher_round.png" - -echo "[SUCCESS] Generated Android placeholder launcher icons:" -echo " - mipmap-mdpi/ic_launcher.png (48x48)" -echo " - mipmap-hdpi/ic_launcher.png (72x72)" -echo " - mipmap-xhdpi/ic_launcher.png (96x96)" -echo " - mipmap-xxhdpi/ic_launcher.png (144x144)" -echo " - mipmap-xxxhdpi/ic_launcher.png (192x192)" -echo " - All round versions created" -echo "[SUCCESS] Android placeholder icon generation completed successfully!" \ No newline at end of file diff --git a/scripts/generate-ios-assets.sh b/scripts/generate-ios-assets.sh deleted file mode 100755 index 17131d2d..00000000 --- a/scripts/generate-ios-assets.sh +++ /dev/null @@ -1,253 +0,0 @@ -#!/bin/bash - -# TimeSafari iOS Asset Generation Script -# Manually generates iOS assets using ImageMagick when capacitor-assets fails -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -IOS_ASSETS_DIR="$PROJECT_ROOT/ios/App/App/Assets.xcassets" -RESOURCES_DIR="$PROJECT_ROOT/resources/ios" - -echo "=== TimeSafari iOS Asset Generation ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Generating iOS assets manually" - -# Check if ImageMagick is available -if ! command -v convert &> /dev/null; then - echo "[ERROR] ImageMagick 'convert' command not found. Please install ImageMagick." - exit 1 -fi - -# Check if source files exist -if [ ! -f "$RESOURCES_DIR/icon/icon.png" ]; then - echo "[ERROR] Source icon not found: $RESOURCES_DIR/icon/icon.png" - exit 1 -fi - -if [ ! -f "$RESOURCES_DIR/splash/splash.png" ]; then - echo "[ERROR] Source splash not found: $RESOURCES_DIR/splash/splash.png" - exit 1 -fi - -if [ ! -f "$RESOURCES_DIR/splash/splash_dark.png" ]; then - echo "[ERROR] Source dark splash not found: $RESOURCES_DIR/splash/splash_dark.png" - exit 1 -fi - -echo "[INFO] Generating iOS app icons..." - -# Generate app icons for different sizes -# iPhone icons -convert "$RESOURCES_DIR/icon/icon.png" -resize 40x40 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-20@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 60x60 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-20@3x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 58x58 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-29@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 87x87 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-29@3x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 80x80 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-40@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 120x120 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-40@3x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 120x120 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-60@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 180x180 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-60@3x.png" - -# iPad icons -convert "$RESOURCES_DIR/icon/icon.png" -resize 20x20 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-20@1x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 40x40 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-20@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 29x29 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-29@1x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 58x58 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-29@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 40x40 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-40@1x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 80x80 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-40@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 152x152 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-76@2x.png" -convert "$RESOURCES_DIR/icon/icon.png" -resize 167x167 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-83.5@2x.png" - -# App Store icon -convert "$RESOURCES_DIR/icon/icon.png" -resize 1024x1024 "$IOS_ASSETS_DIR/AppIcon.appiconset/AppIcon-1024@1x.png" - -echo "[INFO] Generating iOS splash screens..." - -# Generate splash screens for different scales -convert "$RESOURCES_DIR/splash/splash.png" -resize 320x480 "$IOS_ASSETS_DIR/Splash.imageset/splash@1x.png" -convert "$RESOURCES_DIR/splash/splash.png" -resize 640x960 "$IOS_ASSETS_DIR/Splash.imageset/splash@2x.png" -convert "$RESOURCES_DIR/splash/splash.png" -resize 960x1440 "$IOS_ASSETS_DIR/Splash.imageset/splash@3x.png" - -# Generate dark splash screens -convert "$RESOURCES_DIR/splash/splash_dark.png" -resize 320x480 "$IOS_ASSETS_DIR/SplashDark.imageset/splash@1x.png" -convert "$RESOURCES_DIR/splash/splash_dark.png" -resize 640x960 "$IOS_ASSETS_DIR/SplashDark.imageset/splash@2x.png" -convert "$RESOURCES_DIR/splash/splash_dark.png" -resize 960x1440 "$IOS_ASSETS_DIR/SplashDark.imageset/splash@3x.png" - -echo "[INFO] Updating Contents.json files to reference generated images..." - -# Update AppIcon Contents.json to reference the generated files -cat > "$IOS_ASSETS_DIR/AppIcon.appiconset/Contents.json" << 'EOF' -{ - "images" : [ - { - "filename" : "AppIcon-20@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "filename" : "AppIcon-20@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "filename" : "AppIcon-29@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "AppIcon-29@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "filename" : "AppIcon-40@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "AppIcon-40@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "filename" : "AppIcon-60@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "filename" : "AppIcon-60@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "filename" : "AppIcon-20@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "filename" : "AppIcon-20@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "filename" : "AppIcon-29@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "filename" : "AppIcon-29@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "AppIcon-40@1x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "filename" : "AppIcon-40@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "AppIcon-76@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "filename" : "AppIcon-83.5@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "filename" : "AppIcon-1024@1x.png", - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} -EOF - -# Update Splash Contents.json to reference the generated files -cat > "$IOS_ASSETS_DIR/Splash.imageset/Contents.json" << 'EOF' -{ - "images" : [ - { - "filename" : "splash@1x.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "splash@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "splash@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} -EOF - -# Update SplashDark Contents.json to reference the generated files -cat > "$IOS_ASSETS_DIR/SplashDark.imageset/Contents.json" << 'EOF' -{ - "images" : [ - { - "filename" : "splash@1x.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "splash@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "splash@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} -EOF - -echo "[SUCCESS] iOS assets generated successfully!" -echo "[INFO] Generated files:" -find "$IOS_ASSETS_DIR" -name "*.png" | sort - -echo "" -echo "[NOTE] iOS builds require macOS with Xcode - cannot build on Linux" -echo "[INFO] Assets are now ready for when you build on macOS" diff --git a/scripts/purge-generated-assets.sh b/scripts/purge-generated-assets.sh deleted file mode 100755 index 3be21db6..00000000 --- a/scripts/purge-generated-assets.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - -# TimeSafari Generated Assets Purge Script -# Removes generated Android assets and resources from git history -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -echo "=== TimeSafari Generated Assets Purge ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Starting git history cleanup" - -# Check if we're in a git repository -if [ ! -d ".git" ]; then - echo "[ERROR] Not in a git repository. Please run this script from the project root." - exit 1 -fi - -# Check if we have uncommitted changes -if [ -n "$(git status --porcelain)" ]; then - echo "[ERROR] You have uncommitted changes. Please commit or stash them first." - echo "Current status:" - git status --short - exit 1 -fi - -# Create backup branch -BACKUP_BRANCH="backup-before-asset-purge-$(date +%Y%m%d-%H%M%S)" -echo "[INFO] Creating backup branch: $BACKUP_BRANCH" -git branch "$BACKUP_BRANCH" - -echo "[INFO] Starting git filter-branch to remove generated assets..." - -# Use git filter-branch to remove the directories from history -git filter-branch --force --index-filter ' - # Remove generated Android assets directory - git rm -rf --cached --ignore-unmatch android/app/src/main/assets/public/ 2>/dev/null || true - - # Remove generated Android resources (but keep config files) - git rm -rf --cached --ignore-unmatch android/app/src/main/res/drawable*/ 2>/dev/null || true - git rm -rf --cached --ignore-unmatch android/app/src/main/res/mipmap*/ 2>/dev/null || true - git rm -rf --cached --ignore-unmatch android/app/src/main/res/values/ic_launcher_background.xml 2>/dev/null || true - - # Keep configuration files - git add android/app/src/main/res/values/strings.xml 2>/dev/null || true - git add android/app/src/main/res/values/styles.xml 2>/dev/null || true - git add android/app/src/main/res/layout/activity_main.xml 2>/dev/null || true - git add android/app/src/main/res/xml/config.xml 2>/dev/null || true - git add android/app/src/main/res/xml/file_paths.xml 2>/dev/null || true -' --prune-empty --tag-name-filter cat -- --all - -echo "[INFO] Cleaning up git filter-branch temporary files..." -rm -rf .git/refs/original/ -git reflog expire --expire=now --all -git gc --prune=now --aggressive - -echo "[SUCCESS] Generated assets purged from git history!" -echo "[INFO] Backup branch created: $BACKUP_BRANCH" -echo "[INFO] Repository size should be significantly reduced" -echo "" -echo "Next steps:" -echo "1. Test that the repository works correctly" -echo "2. Force push to remote: git push --force-with-lease origin " -echo "3. Inform team members to re-clone or reset their local repositories" -echo "4. Delete backup branch when confident: git branch -D $BACKUP_BRANCH" \ No newline at end of file