Skip to content

Commit e8ba0c7

Browse files
committed
Release automation, club calendar versioning, GitHub Pages web fixes
- Add biennial/10-epoch club calendar version sync (America/Chicago) with club-calendar-version and release-android workflows; eas.json github-apk profile. - Add optional Electron desktop workflow and desktop/ wrapper. - Fix classic dashboard iframe base path for GitHub Pages subpaths. - Redirect away from sign-in when already signed in (outside tabs layout). - Profile shows app version from expo config; app.config reads package version. Made-with: Cursor
1 parent e2b798d commit e8ba0c7

20 files changed

Lines changed: 5786 additions & 16 deletions
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Keeps package.json, app.json, and package-lock.json aligned with club calendar versioning.
2+
# Policy: scripts/club-version-policy.json — America/Chicago date; major every 2 years from Apr 1, 2024;
3+
# 10 epochs per biennium (actual calendar days in that interval); patch = day within epoch.
4+
# Commits use message prefix "chore(version): club calendar" so this workflow does not loop.
5+
#
6+
# When the version changes, also creates GitHub Release vX.Y.Z (triggers release-android.yml).
7+
8+
name: Club calendar version
9+
10+
on:
11+
push:
12+
branches: [main]
13+
workflow_dispatch:
14+
15+
permissions:
16+
contents: write
17+
18+
concurrency:
19+
group: club-calendar-version
20+
cancel-in-progress: true
21+
22+
jobs:
23+
sync:
24+
if: ${{ !contains(github.event.head_commit.message, 'chore(version): club calendar') }}
25+
runs-on: ubuntu-latest
26+
steps:
27+
- name: Checkout (with submodules)
28+
uses: actions/checkout@v4
29+
with:
30+
fetch-depth: 0
31+
submodules: recursive
32+
33+
- name: Setup Node.js
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: "20.x"
37+
cache: npm
38+
39+
- name: Compute and apply club version
40+
id: bump
41+
run: node scripts/sync-club-calendar-version.mjs
42+
43+
- name: Commit version bump
44+
if: steps.bump.outputs.changed == 'true'
45+
run: |
46+
git config user.name "github-actions[bot]"
47+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
48+
git add package.json app.json package-lock.json
49+
git commit -m "chore(version): club calendar ${{ steps.bump.outputs.version }}"
50+
git push
51+
52+
- name: Create GitHub Release
53+
if: steps.bump.outputs.changed == 'true'
54+
env:
55+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56+
run: |
57+
VERSION="${{ steps.bump.outputs.version }}"
58+
gh release create "v${VERSION}" \
59+
--title "BSR Solar Car 2 v${VERSION}" \
60+
--notes "Club calendar version — see \`scripts/club-version-policy.json\` for the numbering rules (America/Chicago date)." \
61+
|| echo "Release v${VERSION} may already exist; skipping."
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Optional: pack the Expo web export as a Windows portable .exe (Electron).
2+
# Run manually: Actions → "Build desktop (Electron)" → Run workflow.
3+
# Uses the same public env secrets as Deploy web to GitHub Pages where applicable.
4+
5+
name: Build desktop (Electron)
6+
7+
on:
8+
workflow_dispatch:
9+
10+
jobs:
11+
windows-portable:
12+
runs-on: windows-latest
13+
steps:
14+
- name: Checkout (with submodules)
15+
uses: actions/checkout@v4
16+
with:
17+
submodules: recursive
18+
19+
- name: Setup Node.js
20+
uses: actions/setup-node@v4
21+
with:
22+
node-version: "20.x"
23+
cache: npm
24+
cache-dependency-path: |
25+
package-lock.json
26+
desktop/package-lock.json
27+
28+
- name: Install root dependencies
29+
run: npm ci
30+
31+
- name: Export web for Electron (no Pages base path)
32+
env:
33+
EXPO_PUBLIC_BASE_PATH: ""
34+
EXPO_PUBLIC_INFLUX_URL: ${{ secrets.EXPO_PUBLIC_INFLUX_URL }}
35+
EXPO_PUBLIC_INFLUX_TOKEN: ${{ secrets.EXPO_PUBLIC_INFLUX_TOKEN }}
36+
EXPO_PUBLIC_INFLUX_ORG: ${{ secrets.EXPO_PUBLIC_INFLUX_ORG }}
37+
EXPO_PUBLIC_INFLUX_BUCKET: ${{ secrets.EXPO_PUBLIC_INFLUX_BUCKET }}
38+
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ secrets.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY }}
39+
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
40+
run: npm run export:web:desktop
41+
42+
- name: Install desktop dependencies
43+
run: npm ci --prefix desktop
44+
45+
- name: Build portable executable
46+
run: npx --prefix desktop electron-builder --win portable
47+
48+
- name: Upload artifact
49+
uses: actions/upload-artifact@v4
50+
with:
51+
name: sc2-desktop-windows-portable
52+
path: desktop/dist/*.exe

.github/workflows/eas-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ on:
2020
options:
2121
- preview
2222
- production
23+
- github-apk
2324

2425
jobs:
2526
build:
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# When a GitHub Release is published (e.g. by club-calendar-version.yml), build Android on EAS and upload the APK.
2+
#
3+
# Prerequisites (one-time):
4+
# 1. expo.dev → Access tokens → create token → repo secret EXPO_TOKEN
5+
# 2. Locally: npx eas-cli login && npx eas-cli build -p android --profile github-apk
6+
# (completes EAS project link, credentials, and writes extra.eas.projectId into app config)
7+
# 3. Commit eas.json and any app.json / app.config.js changes EAS produced.
8+
#
9+
# Private git submodules: if checkout fails, add actions/checkout `token` using a PAT with
10+
# repo access, or switch submodule URLs to HTTPS.
11+
12+
name: Release Android to GitHub
13+
14+
on:
15+
release:
16+
types: [published]
17+
18+
permissions:
19+
contents: write
20+
21+
jobs:
22+
eas-android-apk:
23+
if: ${{ !github.event.release.draft }}
24+
runs-on: ubuntu-latest
25+
steps:
26+
- name: Checkout (with submodules)
27+
uses: actions/checkout@v4
28+
with:
29+
ref: ${{ github.event.release.tag_name }}
30+
fetch-depth: 0
31+
submodules: recursive
32+
33+
- name: Setup Node.js
34+
uses: actions/setup-node@v4
35+
with:
36+
node-version: "20.x"
37+
cache: npm
38+
39+
- name: Install dependencies
40+
run: npm ci
41+
42+
- name: Setup EAS
43+
uses: expo/expo-github-action@v8
44+
with:
45+
eas-version: latest
46+
token: ${{ secrets.EXPO_TOKEN }}
47+
48+
- name: EAS Build (Android APK, wait for completion)
49+
id: eas
50+
run: |
51+
set -euo pipefail
52+
BUILD_JSON=$(eas build \
53+
--platform android \
54+
--profile github-apk \
55+
--non-interactive \
56+
--wait \
57+
--json)
58+
echo "$BUILD_JSON" | tee eas-build.json
59+
URL=$(echo "$BUILD_JSON" | jq -r '
60+
if type == "array" then .[] else . end
61+
| .artifacts.applicationArchiveUrl // empty
62+
' | head -1)
63+
if [ -z "$URL" ] || [ "$URL" = "null" ]; then
64+
echo "Could not read applicationArchiveUrl from EAS output"
65+
cat eas-build.json
66+
exit 1
67+
fi
68+
echo "url=$URL" >> "$GITHUB_OUTPUT"
69+
70+
- name: Download APK
71+
env:
72+
URL: ${{ steps.eas.outputs.url }}
73+
run: |
74+
set -euo pipefail
75+
TAG="${{ github.event.release.tag_name }}"
76+
OUT="BSR-SolarCar2-${TAG}.apk"
77+
curl -fL --retry 3 -o "$OUT" "$URL"
78+
ls -la "$OUT"
79+
80+
- name: Upload APK to release
81+
env:
82+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
83+
run: |
84+
TAG="${{ github.event.release.tag_name }}"
85+
gh release upload "$TAG" "BSR-SolarCar2-${TAG}.apk" --clobber

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ node_modules/
77
.expo/
88
dist/
99
web-build/
10+
desktop/web-dist/
11+
desktop/dist/
1012
expo-env.d.ts
1113

1214
# Native

app.config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const dotenv = require('dotenv');
2+
const pkg = require('./package.json');
3+
24
dotenv.config({ quiet: true });
35

46
module.exports = ({ config }) => {
@@ -7,6 +9,7 @@ module.exports = ({ config }) => {
79

810
return {
911
...config,
12+
version: pkg.version,
1013
experiments: {
1114
...(config.experiments || {}),
1215
...(basePath ? { baseUrl: basePath } : {}),

app.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"expo": {
33
"name": "BSR Solar Car 2",
44
"slug": "sc2-mobile-app",
5-
"version": "1.0.0",
5+
"version": "2.0.1",
66
"orientation": "portrait",
77
"icon": "./assets/icon.png",
88
"userInterfaceStyle": "dark",
@@ -16,15 +16,17 @@
1616
"sdkVersion": "54.0.0",
1717
"ios": {
1818
"supportsTablet": true,
19-
"bundleIdentifier": "com.badgersolarracing.sc2"
19+
"bundleIdentifier": "com.badgersolarracing.sc2",
20+
"buildNumber": "2.0.1"
2021
},
2122
"android": {
2223
"adaptiveIcon": {
2324
"foregroundImage": "./assets/adaptive-icon.png",
2425
"backgroundColor": "#000000"
2526
},
2627
"edgeToEdgeEnabled": true,
27-
"package": "com.badgersolarracing.sc2"
28+
"package": "com.badgersolarracing.sc2",
29+
"versionCode": 20001
2830
},
2931
"web": {
3032
"favicon": "./assets/favicon.png"

app/sign-in.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useEffect } from 'react';
22
import {
33
View,
44
Text,
@@ -23,7 +23,13 @@ WebBrowser.maybeCompleteAuthSession();
2323
export default function SignInScreen() {
2424
const { startOAuthFlow: startGoogleOAuth } = useOAuth({ strategy: 'oauth_google' });
2525
const { startOAuthFlow: startGitHubOAuth } = useOAuth({ strategy: 'oauth_github' });
26-
const { isLoaded: authLoaded } = useAuth();
26+
const { isLoaded: authLoaded, isSignedIn } = useAuth();
27+
28+
// This route is outside (tabs), so useAuthGuard never runs here — close sign-in once Clerk reports a session.
29+
useEffect(() => {
30+
if (!authLoaded || !isSignedIn) return;
31+
router.replace('/');
32+
}, [authLoaded, isSignedIn, router]);
2733
// Legacy SignIn matches useOAuth: create() sets firstFactorVerification.externalVerificationRedirectURL.
2834
// Main useSignIn() is SignInFuture — create() only returns { error }, so OAuth URL was missing on web.
2935
const { signIn: legacySignIn, isLoaded: legacySignInLoaded } = useLegacySignIn();

desktop/main.cjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const { app, BrowserWindow } = require('electron');
2+
const path = require('path');
3+
4+
function createWindow() {
5+
const win = new BrowserWindow({
6+
width: 1280,
7+
height: 800,
8+
webPreferences: {
9+
nodeIntegration: false,
10+
contextIsolation: true,
11+
},
12+
});
13+
win.loadFile(path.join(__dirname, 'web-dist', 'index.html'));
14+
}
15+
16+
app.whenReady().then(createWindow);
17+
18+
app.on('window-all-closed', () => {
19+
app.quit();
20+
});

0 commit comments

Comments
 (0)