forked from jsnbuchanan/crowd-funder-for-time-pwa
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
This commit is contained in:
174
scripts/assets-config.js
Normal file
174
scripts/assets-config.js
Normal file
@@ -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 };
|
||||
218
scripts/assets-validator.js
Normal file
218
scripts/assets-validator.js
Normal file
@@ -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 };
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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'
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/white"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
EOF
|
||||
|
||||
cat > "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher_round.xml" << 'EOF'
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@android:color/white"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
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!"
|
||||
@@ -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!"
|
||||
@@ -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"
|
||||
@@ -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 <branch>"
|
||||
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"
|
||||
Reference in New Issue
Block a user