more work
This commit is contained in:
108
BUILD.md
Normal file
108
BUILD.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# Building RideAware Trainer
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Local Development Build
|
||||||
|
```bash
|
||||||
|
npm run electron:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Builds
|
||||||
|
|
||||||
|
#### Linux (from Linux)
|
||||||
|
```bash
|
||||||
|
npm run electron:build:linux
|
||||||
|
```
|
||||||
|
Builds: AppImage, deb, rpm (requires Ubuntu/Debian for deb/rpm)
|
||||||
|
|
||||||
|
#### Windows (from Windows)
|
||||||
|
```bash
|
||||||
|
npm run electron:build:win
|
||||||
|
```
|
||||||
|
Builds: NSIS installer (.exe)
|
||||||
|
|
||||||
|
#### macOS (from macOS)
|
||||||
|
```bash
|
||||||
|
npm run electron:build:mac
|
||||||
|
```
|
||||||
|
Builds: DMG, ZIP
|
||||||
|
|
||||||
|
## Cross-Platform Builds with GitHub Actions
|
||||||
|
|
||||||
|
The easiest way to build for all platforms is using GitHub Actions:
|
||||||
|
|
||||||
|
### Automatic Release Build
|
||||||
|
```bash
|
||||||
|
git tag v1.0.1
|
||||||
|
git push origin v1.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
This triggers the workflow which:
|
||||||
|
1. Builds on Windows, macOS, and Linux runners
|
||||||
|
2. Creates a GitHub Release
|
||||||
|
3. Uploads all installers (exe, dmg, AppImage, deb, rpm)
|
||||||
|
|
||||||
|
### Manual Build
|
||||||
|
Go to: GitHub → Actions → Build and Release → Run workflow
|
||||||
|
|
||||||
|
## Build Artifacts
|
||||||
|
|
||||||
|
After building, files are in the `release/` directory:
|
||||||
|
|
||||||
|
- **Windows**: `RideAware Trainer Setup 1.0.0.exe`
|
||||||
|
- **macOS**: `RideAware Trainer-1.0.0-arm64.dmg`, `RideAware Trainer-1.0.0-arm64-mac.zip`
|
||||||
|
- **Linux**:
|
||||||
|
- `RideAware Trainer-1.0.0.AppImage` (universal)
|
||||||
|
- `rideaware-trainer_1.0.0_amd64.deb` (Debian/Ubuntu)
|
||||||
|
- `rideaware-trainer-1.0.0.x86_64.rpm` (Fedora/RHEL)
|
||||||
|
|
||||||
|
## Platform Limitations
|
||||||
|
|
||||||
|
### Building from Linux
|
||||||
|
- ✅ Linux packages (AppImage always works, deb/rpm need Ubuntu)
|
||||||
|
- ❌ macOS DMG (requires macOS-specific dependencies)
|
||||||
|
- ❌ Windows EXE (requires Windows or Wine)
|
||||||
|
|
||||||
|
### Building from macOS
|
||||||
|
- ✅ macOS packages (DMG, ZIP)
|
||||||
|
- ✅ Linux packages (with Docker)
|
||||||
|
- ❌ Windows EXE (requires Windows or Wine)
|
||||||
|
|
||||||
|
### Building from Windows
|
||||||
|
- ✅ Windows packages (EXE)
|
||||||
|
- ✅ Linux packages (with WSL/Docker)
|
||||||
|
- ❌ macOS DMG (requires macOS)
|
||||||
|
|
||||||
|
**Recommendation**: Use GitHub Actions for cross-platform builds.
|
||||||
|
|
||||||
|
## Icon Requirements
|
||||||
|
|
||||||
|
Place app icons in the `build/` directory:
|
||||||
|
- `build/icon.ico` - Windows (256x256)
|
||||||
|
- `build/icon.icns` - macOS (1024x1024)
|
||||||
|
- `build/icon.png` - Linux (512x512)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "dmg-license not found" on Linux
|
||||||
|
This is expected. Build macOS packages on macOS or use GitHub Actions.
|
||||||
|
|
||||||
|
### "libcrypt.so.1 not found" on Fedora
|
||||||
|
Install compatibility package or use GitHub Actions (runs on Ubuntu).
|
||||||
|
|
||||||
|
### Build hangs on "packaging"
|
||||||
|
Check available disk space. Electron builds can use 500MB+ per platform.
|
||||||
|
|
||||||
|
## GitHub Actions Workflow
|
||||||
|
|
||||||
|
The workflow file is at `.github/workflows/build.yml`.
|
||||||
|
|
||||||
|
**Triggers:**
|
||||||
|
- Push to `main` branch (builds but doesn't release)
|
||||||
|
- Push tag starting with `v` (builds and creates release)
|
||||||
|
- Manual workflow dispatch
|
||||||
|
- Pull requests (builds but doesn't release)
|
||||||
|
|
||||||
|
**Permissions needed:**
|
||||||
|
- Repository Settings → Actions → General → Workflow permissions
|
||||||
|
- Enable "Read and write permissions"
|
||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
getWorkoutsByMonth,
|
getWorkoutsByMonth,
|
||||||
getTodaysWorkouts,
|
getTodaysWorkouts,
|
||||||
getWeeksWorkouts,
|
getWeeksWorkouts,
|
||||||
markWorkoutComplete,
|
completeWorkout as apiCompleteWorkout,
|
||||||
} from '../services/api';
|
} from '../services/api';
|
||||||
import type { ApiWorkout } from '../types/api';
|
import type { ApiWorkout } from '../types/api';
|
||||||
import type { Workout, WorkoutInterval } from '../types/workout';
|
import type { Workout, WorkoutInterval } from '../types/workout';
|
||||||
@@ -166,17 +166,19 @@ export function useCalendarWorkouts(): UseCalendarWorkoutsReturn {
|
|||||||
maxPower?: number;
|
maxPower?: number;
|
||||||
maxHr?: number;
|
maxHr?: number;
|
||||||
caloriesBurned?: number;
|
caloriesBurned?: number;
|
||||||
|
syncToStrava?: boolean;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
await markWorkoutComplete(workoutId, {
|
await apiCompleteWorkout(workoutId, {
|
||||||
duration: Math.round(metrics.duration / 60),
|
duration: Math.round(metrics.duration),
|
||||||
distance: metrics.distance,
|
distance: metrics.distance,
|
||||||
avg_power: metrics.avgPower,
|
avg_power: metrics.avgPower,
|
||||||
avg_hr: metrics.avgHr,
|
avg_hr: metrics.avgHr,
|
||||||
max_power: metrics.maxPower,
|
max_power: metrics.maxPower,
|
||||||
max_hr: metrics.maxHr,
|
max_hr: metrics.maxHr,
|
||||||
calories_burned: metrics.caloriesBurned,
|
calories_burned: metrics.caloriesBurned,
|
||||||
|
sync_to_strava: metrics.syncToStrava ?? true,
|
||||||
});
|
});
|
||||||
await refresh();
|
await refresh();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { api } from './client';
|
import { api } from './client';
|
||||||
import type { ApiWorkout, WorkoutTemplate, WorkoutUpdatePayload } from '../../types/api';
|
import type { ApiWorkout, WorkoutTemplate, WorkoutUpdatePayload, WorkoutCompletionPayload } from '../../types/api';
|
||||||
|
|
||||||
export async function getWorkouts(): Promise<ApiWorkout[]> {
|
export async function getWorkouts(): Promise<ApiWorkout[]> {
|
||||||
return api.get<ApiWorkout[]>('/protected/workouts');
|
return api.get<ApiWorkout[]>('/protected/workouts');
|
||||||
@@ -35,6 +35,13 @@ export async function markWorkoutComplete(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function completeWorkout(
|
||||||
|
id: number,
|
||||||
|
completionData: WorkoutCompletionPayload
|
||||||
|
): Promise<ApiWorkout> {
|
||||||
|
return api.post<ApiWorkout>(`/protected/workouts/complete?id=${id}`, completionData);
|
||||||
|
}
|
||||||
|
|
||||||
export async function skipWorkout(id: number, notes?: string): Promise<ApiWorkout> {
|
export async function skipWorkout(id: number, notes?: string): Promise<ApiWorkout> {
|
||||||
return api.put<ApiWorkout>(`/protected/workouts?id=${id}`, {
|
return api.put<ApiWorkout>(`/protected/workouts?id=${id}`, {
|
||||||
status: 'skipped',
|
status: 'skipped',
|
||||||
|
|||||||
@@ -151,3 +151,15 @@ export interface WorkoutUpdatePayload {
|
|||||||
calories_burned?: number;
|
calories_burned?: number;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WorkoutCompletionPayload {
|
||||||
|
duration: number;
|
||||||
|
distance?: number;
|
||||||
|
avg_power?: number;
|
||||||
|
avg_hr?: number;
|
||||||
|
max_power?: number;
|
||||||
|
max_hr?: number;
|
||||||
|
calories_burned?: number;
|
||||||
|
notes?: string;
|
||||||
|
sync_to_strava?: boolean;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user