Skip to content

Commit 8bc00ab

Browse files
Add desktop build and deployment scripts
1 parent b217b9e commit 8bc00ab

7 files changed

Lines changed: 441 additions & 0 deletions

File tree

apps/desktop/scripts/clean.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { rm, existsSync } from 'fs';
2+
import { join } from 'path';
3+
import { execSync } from 'child_process';
4+
import { fileURLToPath } from 'url';
5+
import { dirname } from 'path';
6+
7+
const __filename = fileURLToPath(import.meta.url);
8+
const __dirname = dirname(__filename);
9+
10+
const dirsToRemove = ['node_modules/.vite', 'node_modules/.cache', '.cache', 'dist'];
11+
12+
console.log('🧹 Cleaning project...');
13+
14+
// Remove directories
15+
for (const dir of dirsToRemove) {
16+
const fullPath = join(__dirname, '..', dir);
17+
18+
try {
19+
if (existsSync(fullPath)) {
20+
console.log(`Removing ${dir}...`);
21+
rm(fullPath, { recursive: true, force: true }, (err) => {
22+
if (err) {
23+
console.error(`Error removing ${dir}:`, err.message);
24+
}
25+
});
26+
}
27+
} catch (err) {
28+
console.error(`Error removing ${dir}:`, err.message);
29+
}
30+
}
31+
32+
// Run pnpm commands
33+
console.log('\n📦 Reinstalling dependencies...');
34+
35+
try {
36+
execSync('pnpm install', { stdio: 'inherit' });
37+
console.log('\n🗑️ Clearing pnpm cache...');
38+
execSync('pnpm cache clean', { stdio: 'inherit' });
39+
console.log('\n🏗️ Rebuilding project...');
40+
execSync('pnpm build', { stdio: 'inherit' });
41+
console.log('\n✨ Clean completed! You can now run pnpm dev');
42+
} catch (err) {
43+
console.error('\n❌ Error during cleanup:', err.message);
44+
process.exit(1);
45+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#!/usr/bin/env node
2+
3+
import { exec } from 'child_process';
4+
import { promisify } from 'util';
5+
import { existsSync, mkdirSync, rmSync } from 'fs';
6+
7+
const execAsync = promisify(exec);
8+
9+
const sourceIcon = 'public/Assets.xcassets/AppIcon.appiconset/ios-marketing.png';
10+
const outputIcon = 'public/icon.icns';
11+
const iconsetDir = '/tmp/macos_icon.iconset';
12+
13+
async function convertIcon() {
14+
try {
15+
console.log('🎨 Converting iOS icon to macOS .icns...');
16+
17+
// Clean and create iconset directory
18+
if (existsSync(iconsetDir)) {
19+
rmSync(iconsetDir, { recursive: true });
20+
}
21+
mkdirSync(iconsetDir);
22+
23+
// Generate all required sizes
24+
const sizes = [
25+
{ size: 16, name: 'icon_16x16.png' },
26+
{ size: 32, name: 'icon_16x16@2x.png' },
27+
{ size: 32, name: 'icon_32x32.png' },
28+
{ size: 64, name: 'icon_32x32@2x.png' },
29+
{ size: 128, name: 'icon_128x128.png' },
30+
{ size: 256, name: 'icon_128x128@2x.png' },
31+
{ size: 256, name: 'icon_256x256.png' },
32+
{ size: 512, name: 'icon_256x256@2x.png' },
33+
{ size: 512, name: 'icon_512x512.png' },
34+
{ size: 1024, name: 'icon_512x512@2x.png' },
35+
];
36+
37+
console.log('📐 Generating icon sizes...');
38+
for (const { size, name } of sizes) {
39+
const cmd = `sips -z ${size} ${size} "${sourceIcon}" --out "${iconsetDir}/${name}"`;
40+
await execAsync(cmd);
41+
console.log(` ✓ ${name} (${size}x${size})`);
42+
}
43+
44+
// Convert to icns
45+
console.log('🔨 Creating .icns file...');
46+
await execAsync(`iconutil -c icns "${iconsetDir}" -o "${outputIcon}"`);
47+
48+
// Clean up
49+
rmSync(iconsetDir, { recursive: true });
50+
51+
console.log(`✅ Icon created successfully: ${outputIcon}`);
52+
console.log('ℹ️ macOS will automatically apply rounded corners to your app icon');
53+
} catch (error) {
54+
console.error('❌ Error:', error.message);
55+
process.exit(1);
56+
}
57+
}
58+
59+
convertIcon();
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env node
2+
3+
import { exec } from 'child_process';
4+
import { promisify } from 'util';
5+
import fs from 'fs/promises';
6+
7+
const execAsync = promisify(exec);
8+
9+
const dmgSpec = {
10+
title: 'CodinIT.dev',
11+
icon: 'public/icon.png',
12+
background: 'assets/dmg-background.png', // Optional - create if needed
13+
'icon-size': 80,
14+
window: {
15+
size: {
16+
width: 600,
17+
height: 400,
18+
},
19+
},
20+
contents: [
21+
{ x: 180, y: 170, type: 'file', path: 'dist/CodinIT.dev-darwin-arm64/CodinIT.dev.app' },
22+
{ x: 420, y: 170, type: 'link', path: '/Applications' },
23+
],
24+
};
25+
26+
async function createDMG() {
27+
try {
28+
console.log('🔨 Creating DMG specification...');
29+
30+
// Write DMG spec
31+
await fs.writeFile('dmg-spec.json', JSON.stringify(dmgSpec, null, 2));
32+
33+
console.log('📦 Building DMG...');
34+
35+
// Create DMG
36+
const { stdout, stderr } = await execAsync('npx appdmg dmg-spec.json dist/CodinIT.dev.dmg');
37+
38+
if (stdout) {
39+
console.log(stdout);
40+
}
41+
42+
if (stderr) {
43+
console.error(stderr);
44+
}
45+
46+
console.log('✅ DMG created successfully: dist/CodinIT.dev.dmg');
47+
48+
// Clean up
49+
await fs.unlink('dmg-spec.json');
50+
} catch (error) {
51+
console.error('❌ Error creating DMG:', error.message);
52+
process.exit(1);
53+
}
54+
}
55+
56+
createDMG();
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Electron Development Script
5+
*
6+
* This script provides hot-reload development mode for Electron applications.
7+
* It automatically builds Electron dependencies, starts the Remix development server,
8+
* and launches the Electron application with hot-reload capabilities.
9+
*/
10+
11+
import { spawn, exec } from 'node:child_process';
12+
import path from 'node:path';
13+
import fs from 'node:fs';
14+
import { fileURLToPath } from 'node:url';
15+
16+
// Get current file directory
17+
const __filename = fileURLToPath(import.meta.url);
18+
const __dirname = path.dirname(__filename);
19+
20+
// Constants
21+
const REMIX_PORT = 5173;
22+
const CHECK_INTERVAL = 1000;
23+
const MAX_RETRIES = 30;
24+
25+
// Set environment variables
26+
process.env.NODE_ENV = 'development';
27+
28+
console.log('🚀 Starting Electron hot-reload development mode...');
29+
console.log('🔧 Environment:', process.env.NODE_ENV);
30+
31+
let electronProcess = null;
32+
let remixProcess = null;
33+
34+
/**
35+
* Cleanup function to gracefully shutdown all processes
36+
*/
37+
function cleanup() {
38+
console.log('\n🛑 Shutting down all processes...');
39+
40+
if (electronProcess) {
41+
electronProcess.kill('SIGTERM');
42+
}
43+
44+
if (remixProcess) {
45+
remixProcess.kill('SIGTERM');
46+
}
47+
48+
process.exit(0);
49+
}
50+
51+
// Handle process exit signals
52+
process.on('SIGINT', cleanup);
53+
process.on('SIGTERM', cleanup);
54+
55+
/**
56+
* Wait for a server to start on the specified port
57+
* @param {number} port - Port number to check
58+
* @param {string} serverName - Name of the server for logging
59+
* @returns {Promise<void>}
60+
*/
61+
async function waitForServer(port, serverName) {
62+
return new Promise((resolve, reject) => {
63+
let retries = 0;
64+
65+
const checkServer = () => {
66+
exec(`lsof -i :${port}`, (error, stdout) => {
67+
if (stdout) {
68+
console.log(`✅ ${serverName} started`);
69+
resolve();
70+
} else if (retries >= MAX_RETRIES) {
71+
reject(new Error(`Timeout waiting for ${serverName} to start`));
72+
} else {
73+
retries++;
74+
setTimeout(checkServer, CHECK_INTERVAL);
75+
}
76+
});
77+
};
78+
79+
checkServer();
80+
});
81+
}
82+
83+
/**
84+
* Build Electron dependencies
85+
* @returns {Promise<void>}
86+
*/
87+
async function buildElectronDeps() {
88+
return new Promise((resolve, reject) => {
89+
const buildProcess = spawn('pnpm', ['electron:build:deps'], {
90+
stdio: 'inherit',
91+
env: { ...process.env },
92+
});
93+
94+
buildProcess.on('close', (code) => {
95+
if (code === 0) {
96+
console.log('✅ Electron dependencies built successfully');
97+
resolve();
98+
} else {
99+
reject(new Error(`Build failed with exit code: ${code}`));
100+
}
101+
});
102+
103+
buildProcess.on('error', (error) => {
104+
reject(new Error(`Build process error: ${error.message}`));
105+
});
106+
});
107+
}
108+
109+
/**
110+
* Main function to start Electron development mode
111+
* @returns {Promise<void>}
112+
*/
113+
async function startElectronDev() {
114+
try {
115+
// 1. Build Electron dependencies first
116+
console.log('📦 Building Electron dependencies...');
117+
await buildElectronDeps();
118+
119+
// 2. Start Remix development server
120+
console.log('🌐 Starting Remix development server...');
121+
remixProcess = spawn('pnpm', ['dev'], {
122+
stdio: 'pipe',
123+
env: { ...process.env },
124+
});
125+
126+
remixProcess.stdout.on('data', (data) => {
127+
const output = data.toString();
128+
console.log(`[Remix] ${output.trim()}`);
129+
});
130+
131+
remixProcess.stderr.on('data', (data) => {
132+
console.error(`[Remix Error] ${data.toString().trim()}`);
133+
});
134+
135+
// Wait for Remix server to start
136+
await waitForServer(REMIX_PORT, 'Remix development server');
137+
138+
// 3. Start Electron application
139+
console.log('⚡ Starting Electron application...');
140+
141+
const electronPath = path.join(__dirname, '..', 'node_modules', '.bin', 'electron');
142+
const mainPath = path.join(__dirname, '..', 'build', 'electron', 'main', 'index.mjs');
143+
144+
// Check if main process file exists
145+
if (!fs.existsSync(mainPath)) {
146+
throw new Error(`Main process file not found: ${mainPath}`);
147+
}
148+
149+
electronProcess = spawn(electronPath, [mainPath], {
150+
stdio: 'inherit',
151+
env: {
152+
...process.env,
153+
NODE_ENV: 'development',
154+
ELECTRON_IS_DEV: '1',
155+
},
156+
});
157+
158+
electronProcess.on('error', (error) => {
159+
console.error('❌ Failed to start Electron:', error);
160+
cleanup();
161+
});
162+
163+
electronProcess.on('exit', (code) => {
164+
console.log(`📱 Electron process exited with code: ${code}`);
165+
166+
if (code !== 0) {
167+
cleanup();
168+
}
169+
});
170+
171+
console.log('🎉 Electron hot-reload development mode started!');
172+
console.log('💡 Code changes will automatically reload');
173+
console.log('🛑 Press Ctrl+C to exit');
174+
} catch (error) {
175+
console.error('❌ Startup failed:', error.message);
176+
cleanup();
177+
}
178+
}
179+
180+
// Start development mode
181+
startElectronDev();

apps/desktop/scripts/setup-env.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/bash
2+
3+
# Setup script for Docker environment variables
4+
# This script helps resolve the issue where .env.local is not being loaded in Docker
5+
6+
echo "🔧 Setting up environment files for Docker..."
7+
8+
# Check if .env.local exists
9+
if [ -f ".env.local" ]; then
10+
echo "✅ Found .env.local file"
11+
12+
# Create symlink or copy to .env for Docker Compose
13+
if [ ! -f ".env" ] || [ ".env" -ot ".env.local" ]; then
14+
echo "📋 Copying .env.local to .env for Docker Compose compatibility..."
15+
cp .env.local .env
16+
echo "✅ Environment file synced"
17+
else
18+
echo "ℹ️ .env file already up to date"
19+
fi
20+
else
21+
echo "⚠️ No .env.local file found"
22+
23+
# Check if .env.example exists and offer to copy it
24+
if [ -f ".env.example" ]; then
25+
echo "📋 Found .env.example file"
26+
read -p "Would you like to create .env.local from .env.example? (y/n) " -n 1 -r
27+
echo
28+
if [[ $REPLY =~ ^[Yy]$ ]]; then
29+
cp .env.example .env.local
30+
cp .env.example .env
31+
echo "✅ Created .env.local and .env from .env.example"
32+
echo "📝 Please edit .env.local with your API keys"
33+
fi
34+
fi
35+
fi
36+
37+
echo "✨ Environment setup complete!"
38+
echo ""
39+
echo "📚 Note: Docker Compose reads both .env and .env.local files"
40+
echo " - .env is used for variable substitution in docker-compose.yaml"
41+
echo " - .env.local is passed to the container for runtime variables"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
3+
# Update imports in TypeScript files
4+
find app -type f -name "*.ts" -o -name "*.tsx" | xargs sed -i '' 's|~/components/settings/settings.types|~/components/@settings/core/types|g'
5+
6+
# Update imports for specific components
7+
find app -type f -name "*.ts" -o -name "*.tsx" | xargs sed -i '' 's|~/components/settings/|~/components/@settings/tabs/|g'

0 commit comments

Comments
 (0)