Version 0.6 — desktop + web UI, safe command execution, and dynamic day focus from your local course clone.
A helpful desktop/web companion app for Code Platoon AI DevOps students. It guides you through the correct GitHub workflow for each day's lesson, lab, and challenge so you build strong professional habits from day one.
- Product name (in the app): GithubBuddy
- GitHub repository:
GithubBuddy(npm packagegithubbuddy) — clone withgit clone https://github.com/YOUR_USERNAME/GithubBuddy.git, thencd GithubBuddy(your local folder name matches the repo). - npm package name:
githubbuddy(seepackage.json).
Updating from an older clone: workspace/upstream keys in browser storage were renamed; you may need to choose your workspace folder again once.
License: MIT — required for redistribution; keep the file in the repo root.
Contributing: See CONTRIBUTING.md (PR workflow, checks, releases/tags).
Course workflow reference: GIT Challenge Submission Workflow (.docx in docs/) — official-style submission steps for Git challenges; pairs with the guided checklist in the app. See also the docs index.
Maintainers: On GitHub, open the repo About settings and add a short description, topics (for example education, electron, react, vite, typescript, code-platoon), and confirm the License badge shows MIT. Keep the repository name aligned with GithubBuddy so clone URLs and this README stay consistent.
In the Code Platoon course, instructors often say things like:
"Clone today's files, create a branch or fork, complete the challenge, then open a PR to your fork for review."
Many students are new to the terminal and GitHub best practices. This app removes the guesswork by giving you:
- A clear, step-by-step checklist for the exact day you're on
- Plain-English explanations of why each step follows professional GitHub workflow
- Ready-to-copy terminal /
ghcommands - One-click Run buttons that execute allowlisted commands in your chosen workspace (with confirmation and streamed output in the desktop app)
Goal: Turn every daily task into a repeatable, best-practice habit instead of copy-pasting commands you don't fully understand.
This project does not only teach GitHub habits—it follows the official Code Platoon Instructor GIT Challenge Submission Workflow in what we show students: wXdX-challenges branch names, per-challenge folders under weekX/dayY/, the exact commit message format (Complete week X day Y challenge Z), PR title pattern (WXDX - Challenge N - Your Name), confirming origin / upstream, the TA review complete label, and the rule that new pushes update an open PR.
- Checklist copy and commands live in
src/data/days.tsand match that guide 1:1 (with{{WEEK}}/{{DAY}}interpolation). - The instructor
.gitignoreappendix is checked in asdocs/instructor-gitignore-template.txt(same content as the Word guide); see alsodocs/git-challenge-submission-workflow.docx.
This section is deliberately honest: students who run git log should not find a story in the README that the history contradicts.
- Linear history on
main. Through v0.6, changes were integrated by pushing commits directly tomain, not by merging a series of pull requests. That is a common pattern while bootstrapping a small educational codebase; it is not the same as full GitHub Flow with review on every change. - Conventional Commits (
feat:,fix:,docs:, …) are used in commit messages, which matches the habit the app promotes. - Topic branches (for example
polish) may exist for experiments; they are not yet the primary integration path for this upstream repo.
The in-app guidance describes the workflow you should use in your own work: short-lived feature branches, pull requests, and keeping main in good shape. Do that on your fork and in cohort assignments—the checklist is the source of truth for student practice.
To align this repository with what we teach:
- Stop landing routine work only via direct pushes to
main; open PRs and use merge (or squash-merge) so history shows integration points. - Turn on branch protection on
main(see Enabling Branch Protection below) so CI must pass and (optionally) review is required. - Keep using Conventional Commits and clear PR descriptions.
Until those steps are routine here, treat the app as teaching professional GitHub habits; treat this upstream repo’s history as catching up to that standard—not as proof it was always done that way.
References:
| Language | Why We Use It | Documentation Referenced |
|---|---|---|
| TypeScript | Type safety catches mistakes early. Excellent IDE support and autocompletion. Industry standard for serious React apps. | TypeScript Handbook |
| React 19 | Component-based UI. Fast, declarative, and has a huge ecosystem. Perfect for building interactive checklists and day selectors. | React Docs |
| CSS / Modern CSS | We will use clean, scoped styles (and optionally Tailwind later) for a pleasant learning experience. | MDN CSS |
| Package | Category | Why We Use It | Version (current) |
|---|---|---|---|
| Vite | Build Tool | Extremely fast dev server and hot module replacement. Official React template. | ^8.0.10 |
| @vitejs/plugin-react | Vite Plugin | Enables React Fast Refresh and JSX transformation. | ^6.0.1 |
| Electron | Desktop Runtime | Turns our web app into a real Mac (and Windows/Linux) desktop application. | ^42.0.1 |
| electron-vite | Electron + Vite | Bundles main + preload + renderer; see electron.vite.config.ts. |
^5.0.0 |
| vite-plugin-electron-renderer | Vite Plugin | Electron renderer config only; shims Node/Electron in the Vite bundle. | ^0.14.7 |
| TypeScript ESLint | Linting | Catches common bugs and enforces consistent code style. | ^8.58.2 |
| ESLint + React plugins | Linting | React-specific rules (hooks, refresh) to keep code clean. | Various |
- Deeper Playwright flows (Electron window, filesystem) beyond the current browser visual suite
Already in use: Vitest and React Testing Library (npm test), plus Playwright for browser visual inventory, lightweight interaction smoke tests, and full-page screenshots (npm run test:e2e). Setup follows the official Playwright installation guide; install browsers once with npx playwright install chromium.
githubbuddy/
├── src/
│ ├── components/ # Reusable UI pieces (DaySelector, StepCard, CommandBlock, etc.)
│ ├── data/ # JSON or TypeScript files describing each day's guidance
│ ├── hooks/ # Custom React hooks (useDayGuidance, useClipboard, etc.)
│ ├── utils/ # Helper functions (command runner, path helpers)
│ ├── App.tsx # Main layout
│ └── main.tsx # React entry point
├── electron/ # Electron main + preload (IPC, filesystem, git helpers)
├── public/
├── package.json
├── vite.config.ts
├── tsconfig.json
└── README.md
- Open the app (runs in browser during development, later as a desktop app).
- Enter the day you're working on (e.g., "Week 2, Day 4" or simply "W2D4").
- App looks up the corresponding guidance data.
- You see a beautiful, numbered checklist with:
- Step title (e.g., "Create and check out the challenge branch for this day")
- Best-practice explanation (why the step matches the instructor workflow)
- The exact command to run (e.g.
git checkout -b w2d4-challenges) - A big "Copy" button
- Run from the app (desktop): Choose your workspace folder → use Run Command on a step (confirmation + streamed output). The web build still uses copy-to-clipboard for commands.
All daily guidance lives in easy-to-edit files under src/data/. This means:
- Instructors or students can add new days without touching React code.
- We can start with Week 2 Day 4 as the first complete example.
- ✅ Vite + React 19 + TypeScript app with ESLint and tests
- ✅ Electron desktop app (dev, build, production preview) with secure preload IPC
- ✅ Workspace folder picker, command preview, and streaming command execution
- ✅ Dynamic week/day scanning and day focus markdown loaded from a local course clone (via preload)
- ✅ Guidance panel checklist with persisted progress; upstream repo setup script (
npm run setup-course) - ✅ Playwright browser inventory + interaction smoke in CI (optional screenshot baselines locally)
- 🔜 Broader day coverage; optional Electron-window E2E if you want stricter release checks
Use this sequence when you first clone the repo or when you want to confirm everything works end-to-end.
-
Prerequisites — Node.js 22.12+ and Git installed (see
package.jsonengines); clone this repository (your fork or the cohort remote you were given), e.g.git clone https://github.com/YOUR_USERNAME/githubbuddy.git, thencd githubbuddy. -
Install dependencies
npm install
-
Course content (required for dynamic day focus) — Run the guided setup once (or ensure the clone exists at the path the app expects):
npm run setup-course
Alternatively, clone the upstream course repo into
data/course-content/aico-echo(see v0.6 – Dynamic Day Focus from Upstream Repo under Roadmap below for the one-line clone example). -
Web smoke test
npm run dev
Open the app in the browser, pick a week/day, confirm the checklist and copy commands work. If the course clone is present, confirm day focus content loads.
-
Desktop smoke test
npm run electron:dev
Confirm the native window shows the same UI, workspace selection, and (if applicable) Fetch upstream / refreshed scan after pulling course content.
-
Quality gate (before a PR) — matches CI (
eslint, Vitest with coverage thresholds, production web build, Electron bundle build, Playwright inventory + interactions on Ubuntu):npm run lint npm run test:coverage npm run build npm run test:e2e:ci
For a quicker loop while coding,
npm test(watch) ornpm test -- --runskips the coverage threshold check. Full Playwright + screenshot baselines:npm run test:e2e(see e2e/visual/user-facing-inventory.ts and e2e/functional/app-interactions.spec.ts); refresh PNGs withnpm run test:e2e:update-snapshots. -
Production-style desktop check (catches wrong dev-server URL or stale build output)
npm run electron:build npm run electron:preview
If the window is blank, run
npm run electron:clean, repeat step 7, and see Problem 5 in Electron troubleshooting. -
Ship it (recommended) — On your fork, use a short-lived feature branch, push, and open a Pull Request into
mainafter CI is green—the same habit the app’s checklist reinforces. Upstream maintainers are encouraged to adopt the same PR-based integration for this repo (see Git workflow: history, teaching, and next steps).
cd ~/Desktop/githubbuddy
npm install # if you haven't already
npm run dev # starts Vite dev server at http://localhost:5173Open the URL in your browser. Select Week 2 → Day 4. You will see the full guided checklist with explanations and copy buttons. Check off steps as you complete them — your progress is saved automatically.
All daily guidance lives in a single file: src/data/days.ts.
- Open
src/data/days.ts - Add a new entry to the
daysobject using the key formatW{week}D{day}(example:W3D1). - Use the
DayGuidanceinterface (defined insrc/types/index.ts). - Provide 5–8 steps with clear
title,why(best-practice explanation),command, and optionalnotes. - Add 4–6 items to
bestPractices.
Example skeleton:
'W3D2': {
week: 3,
day: 2,
title: 'Week 3 - Day 2: ...',
summary: 'One sentence overview...',
steps: [ /* array of Step objects */ ],
bestPractices: [ /* strings */ ]
}After saving, the new day immediately appears in the dropdowns. No rebuild required in dev mode.
GithubBuddy can be run in two ways:
- Web version (opens in your browser) – great for quick development and learning the UI.
- Desktop version (real native app window) – the full experience with future command execution.
- Node.js 22.12+ (required by the pinned
electronmajor; use current Node 22 LTS) - Git
- For desktop: nothing extra needed — Electron downloads its own binaries on first run
Without data/course-content/aico-echo, you still get the in-app checklist and copyable commands; you only miss the Course materials panel (full README / lesson markdown per day) and dynamic week/day dropdowns driven by the clone. See INSTALL.md → If Day focus or the week/day list looks “empty” for the short troubleshooting path.
To enable the app to load the actual lesson, lab, and challenge content from the upstream repo, run:
npm run setup-courseThe script will:
- Prompt you for the upstream repo URL
- Explain private vs public repo security implications
- Guide you on using Personal Access Tokens or SSH keys (GitHub best practices)
- Clone the repo into the correct location
Security Reminder: The upstream repo is private. Use a fine-grained PAT with minimal scopes or an SSH key. Never commit credentials.
Official GitHub guidance:
# 1. Clone the repository (or download the zip)
git clone https://github.com/YOUR_USERNAME/githubbuddy.git
cd githubbuddy
# 2. Install all dependencies (this also installs Electron for later)
npm install
# 3. Start the development server
npm run devThe app will open automatically in your default browser at http://localhost:5173.
On Windows: Use the exact same commands in PowerShell, Command Prompt, or Git Bash.
# After you have run `npm install` once:
# Start both the web dev server and the native desktop window together
npm run electron:devThis command:
- Starts Vite on port 5173 (with hot reload)
- Opens a native application window (Windows or macOS)
- Opens DevTools automatically so you can inspect the React app
First run on any computer will download the Electron binary for your operating system (≈ 100–150 MB). This only happens once.
On Windows: The same npm run electron:dev command works identically and opens a native Windows window.
| Command | What it does | When to use |
|---|---|---|
npm run dev |
Web-only development server | Quick UI work in browser |
npm run electron:dev |
Full desktop experience with hot reload | Daily development of the app |
npm run build |
Production build of the web app | Before packaging for distribution |
npm run electron:build |
Build main/preload/renderer into dist-electron/ + out/renderer |
Before testing production UI locally |
npm run electron:preview |
Run Electron against the built UI (cross-env NODE_ENV=production electron .) |
After electron:build — avoids white screen from loading a dead dev server |
npm run electron:clean |
Deletes out/ and dist-electron/ |
When index.html or build output seems stale; then rebuild |
npm run lint |
ESLint across the repo | CI and before PRs |
npm test |
Run the automated test suite | Before opening a Pull Request |
- All commands above work on Windows, macOS, and Linux.
electron:previewusescross-envsoNODE_ENV=productionis set correctly on Windows too.- We deliberately use cross-platform tools (
electron-vite,cross-env) so students on any operating system have the same experience. - No platform-specific code or setup is required for the core desktop flow.
Official documentation:
To ensure we are following current best practices, we consulted:
- Vite Official Documentation
- Electron Official Documentation
- electron-vite GitHub Repository & Docs
- React 19 Release Notes & Docs
- TypeScript Handbook
- GitHub Best Practices:
- GitHub CLI (
gh) Documentation - GitHub Flow
- Pro Git Book (especially branching and remotes chapters)
- GitHub CLI (
- MDN Web Docs for HTML/CSS/JS fundamentals
We believe in testing early so the app stays reliable as we add features.
npm test— Vitest watch mode for developmentnpm run test:coverage— same suite with V8 coverage; must meet minimum thresholds in vitest.config.ts (CI runs this, not 100% gate — raise thresholds as coverage improves)npm run test:e2e:ci— Playwright visual inventory plus a small interaction suite (badge updates, empty state, checklist checkbox when guidance is active — see e2e/visual/user-facing-inventory.ts and e2e/functional/app-interactions.spec.ts)npm run test:e2e— full Playwright run including full-page screenshot comparisons (baselines are OS-specific; Chromium snapshots live next to e2e/visual/screenshots.spec.ts)
-
Unit tests — Pure logic: day data (
days.ts), command templates (commandInterpolation.ts), course scanning (courseScanner.ts), upstream URL validation (upstreamRepoUrl.ts), shell allowlisting (electron/runShellCommand.ts), preload-only disk helpers (electron/courseContentScan.ts,electron/dayFocusLoader.ts), storage helpers, clipboard wrapper, etc. -
UI component tests (React Testing Library) — Exercises real DOM and user-visible behavior for:
- App.tsx, main.tsx bootstrap
- DaySelector.tsx (including Fetch upstream and invalid URL handling)
- WorkspaceSelector.tsx, UpstreamPathSelector.tsx
- GuidancePanel.tsx, StepCard.tsx, CommandOutput.tsx, ProgressTracker.tsx, DayFocus.tsx, Tooltip.tsx, toast layer (ToastProvider.tsx, useToast.ts)
- Hooks useDayGuidance, useDayFocus
-
Playwright (browser) — Visual presence coverage for the Vite app (header, day/workspace/upstream chrome, toast region, footer, and branch-specific UI: guidance checklist with nine steps, Day focus when
data/course-contentexists, no-guidance for Week 3 Day 1, plus Run affordances when workspace is seeded viae2e/storage/workspace-selected.json). Interaction smoke tests cover selector-driven UI updates and checklist toggling when the guidance layout is active. Does not launch Electron; Fetch upstream is desktop-only and is intentionally out of scope here. -
Electron entrypoints (
electron/main.ts,electron/preload.ts) — Not unit-tested in isolation; they are thin wiring layers. We rely on production build (npm run electron:build), lint/typecheck, and the desktop smoke checklist for those paths. Fetch upstream still validates URLs in the main process (sameresolveValidatedUpstreamUrlhelper) so the UI cannot be the only gate.
| Area | Examples |
|---|---|
| Upstream fetch URL | Blank prompt → default Code Platoon repo; valid https://github.com/... or git@github.com:...; rejected GitLab/other hosts, malformed URLs, and strings with shell metacharacters (;, `, $, ..) so git clone is never given an injectable string. |
| Dialogs | User cancels folder picker / prompt → no stale state; Run-all cancel on confirm → no commands run. |
| Errors & empty states | Fetch failure / thrown IPC; missing guidance day; empty DayFocus file list; clipboard failures; command execution rejection. |
| Course content | Browser vs Electron scan; missing getCourseContentScan; loader throws; window undefined (SSR-safe branch). |
- Accessibility — Keyboard order, labels, focus traps in modals (we use native
confirm/prompttoday), screen reader text for dynamic status. - Environments — Windows vs macOS paths, Electron vs pure browser (
npm run dev), offline / auth failures forgit/gh. - Performance & resilience — Large command output (capped in main), timeouts, rapid double-clicks.
- Localization & copy — Clear error strings (students must know what to fix).
- Security — Treat all user-provided strings as untrusted until validated (upstream URL, future free-text fields).
- E2E — We already run browser Playwright in CI (see above). Full Electron-window automation is still optional for class use.
Integration / E2E (optional stricter step)
- Full workflow in a real Electron window: select day → fetch (or skip) → run a harmless
gitcommand in a temp repo (not in CI today — manual desktop smoke checklist covers this).
Current State: Unit + component coverage is broad and enforced at minimum percentages in CI (not 100% globally). Run npm run test:coverage locally for the latest table; see Proof of Correctness.
- v0.1 (Completed): React web app with day selector + Week 2 Day 4 guidance + copy buttons
- v0.2 (Completed): Workspace folder picker + safe command execution preview
- v0.3 (Completed): Desktop app packaging with Electron (Windows + macOS support)
- v0.4 (Completed): Run commands from the checklist via Electron IPC (preview + confirmation + workspace scope)
- v0.5 (Completed): Streaming command output (
spawn+ IPC events) - v0.6 (Completed): Dynamic day focus from local course clone; preload-based filesystem scan; production desktop load path fixes; guidance panel + setup script
- v1.0 (Next): More day coverage in
days.ts, UI polish, optional Electron-driven E2E
In v0.5 we upgraded the command execution from batch (exec) to real-time streaming (spawn).
Key improvements:
- Output appears live in the UI as the command runs
- Separate handling for stdout and stderr
- Uses proper IPC event streaming (
command-outputandcommand-completeevents)
Documentation followed:
This makes longer commands (git clone, builds, tests) feel responsive.
After code changes to IPC or command execution, verify quickly on your machine:
- Run
npm run electron:devand wait for the window. - Click Choose Workspace Folder and select a real local git repo (your fork).
- Optionally set Course / upstream folder and save, so
{{UPSTREAM}}resolves in copy steps. - Pick Week 2 · Day 1 (or any day with guidance), then use Run Command on a safe step (for example one that only runs
git statusif you add it for testing), or use Run all runnable steps only in a throwaway repo. - Confirm you see streamed output and that a failing command stops Run all with a message in the batch log.
Automated coverage: npm test (data helpers, allowlist/segment parsing, and React pieces with mocked window.electronAPI).
We have now implemented the ability to actually run the commands shown in the checklist directly from the app.
How it works (the documented way):
We follow the official electron-vite + Electron security model:
-
Preload Script (
electron/preload.ts)- Uses
contextBridgeto safely expose two methods to the React app. - Reference: Context isolation
- Uses
-
Main Process (
electron/main.ts+electron/runShellCommand.ts)- Handles IPC and runs commands with
child_process.spawn(no shell; argv array), allowlist checks, timeouts, and output caps. - Reference: ipcMain
- Handles IPC and runs commands with
-
Configuration (
electron.vite.config.ts)- Properly configured renderer with
rollupOptions.input: 'index.html'because our project has the HTML file at the root (standard Vite layout). - Reference: electron-vite config
- Properly configured renderer with
-
UI Layer
WorkspaceSelectornow uses the real native dialog.StepCardshows a green Run Command button when a workspace is selected.- Clicking Run shows a confirmation dialog with the exact command (preview).
- Results (stdout/stderr) are displayed below the step using the new
CommandOutputcomponent.
Safety features in v0.4+:
- Commands are never executed without explicit user confirmation (per-step Run; Run all asks once with a full list).
- The full command is shown in the confirmation dialog.
- Execution uses the user-selected workspace folder as
cwd. - Allowlisted prefixes (
git,gh,mkdir,cp,echo) plus segment parsing; timeout and captured-output limits inrunShellCommand.
Files added/modified in v0.4:
electron/preload.ts– Safe API bridgeelectron/main.ts– IPC handlers + command executionsrc/components/WorkspaceSelector.tsx– Native dialog supportsrc/components/StepCard.tsx– Run button + confirmationsrc/components/CommandOutput.tsx– Result displaysrc/types/electron.d.ts– TypeScript declarations forwindow.electronAPI
Key documentation references used:
- Context Isolation & Preload Scripts
- IPC (Renderer ↔ Main)
- dialog.showOpenDialog
- child_process.spawn
- Security Best Practices
The app can now load the full content of the lesson, lab, and challenge files directly from your local clone of the upstream course repository.
How it works:
- Select a Week and Day in the UI.
- If you have cloned
https://github.com/CodePlatoon/aico-echointodata/course-content/aico-echo, the app automatically reads all Markdown files in that day’s folder. - The full content is displayed instead of (or alongside) the app’s educational guidance.
- When you use Fetch upstream in the desktop app, a read-only marker file
GITHUBBUDDY_UPSTREAM.txtmay appear in the course clone root (recording which URL was used). Older app versions createdPLATOON_COMPANION_UPSTREAM.txt; a successful fetch removes that legacy file. You can delete either marker or add the name to.gitignoreif you prefer.
Setup (one-time):
git clone https://github.com/CodePlatoon/aico-echo.git data/course-content/aico-echoWhen the local clone is present, the DaySelector shows “full day focus loaded”.
If the clone is missing, the desktop app shows a short note under Choose your day and INSTALL.md documents the exact folder path and setup-course / Fetch upstream flow.
Documentation followed:
We implemented the foundation for running GithubBuddy as a native macOS (and Windows/Linux) desktop application using Electron.
New commands added:
# Run the app as a real desktop window with hot reload
npm run electron:dev
# Build the desktop app for distribution
npm run electron:buildWhat we did (the documented way):
- Created
electron/main.ts– the main process that creates the native window - Created
electron/preload.ts– the secure bridge (following context isolation best practices) - Created
electron.vite.config.ts– the officialelectron-viteconfiguration - Updated
package.jsonwith proper Electron scripts and entry point
All new files contain extensive educational comments with direct links to the official documentation.
Key documentation links for learning:
- Electron Process Model
- BrowserWindow API
- Security Best Practices
- electron-vite Official Guide
- Context Isolation & Preload Scripts
This puts us on track to deliver a polished desktop experience while teaching students the correct, secure way to build cross-platform desktop apps.
The GithubBuddy desktop app is designed to run on both Windows and macOS (and Linux) using the same codebase.
Why this works (the documented way):
- Electron abstracts the differences between operating systems.
- The renderer process (our React app) runs inside Chromium on every platform.
- We use cross-platform Node.js tools (
concurrently,wait-on,electron-vite) for the development scripts. - No platform-specific code paths are needed for the core functionality.
How to run on Windows:
Students on Windows use the exact same commands:
cd githubbuddy
npm install
npm run electron:devThe app will open a native Windows window with the same UI and functionality.
Official documentation for cross-platform Electron development:
We have deliberately avoided any macOS-only or Windows-only APIs in v0.3 so the learning experience is identical regardless of the student's operating system.
To prove the core logic works, we added a minimal but meaningful test suite using Vitest (Vite-native, extremely fast) and React Testing Library (for future component tests).
- Installed:
vitest,@testing-library/react,@testing-library/jest-dom,jsdom - Added
"test": "vitest"script topackage.json - Created src/data/days.test.ts – tests the data lookup function that powers the entire app
/**
* Automated tests for the days data layer.
* These tests prove that getDayGuidance works correctly for existing and missing days.
*/
import { describe, it, expect } from "vitest";
import { getDayGuidance, days } from "./days";
describe("getDayGuidance", () => {
it("should return the Week 2 Day 4 guidance when requested", () => {
const guidance = getDayGuidance(2, 4);
expect(guidance).toBeDefined();
expect(guidance?.week).toBe(2);
expect(guidance?.day).toBe(4);
expect(guidance?.steps.length).toBe(7);
});
it("should return undefined for a day that has not been created yet", () => {
const guidance = getDayGuidance(99, 99);
expect(guidance).toBeUndefined();
});
it("should expose all authored days via the days export", () => {
expect(Object.keys(days)).toContain("W2D4");
});
}); RUN v4.1.5 /Users/you/dev/githubbuddy
✓ src/data/days.test.ts > getDayGuidance > should return the Week 2 Day 4 guidance when requested 1ms
✓ src/data/days.test.ts > getDayGuidance > should return undefined for a day that has not been created yet 0ms
✓ src/data/days.test.ts > getDayGuidance > should expose all authored days via the days export 0ms
Test Files 1 passed (1)
Tests 3 passed (3)
Start at 14:33:59
Duration 81ms (transform 15ms, setup 0ms, import 20ms, tests 1ms, environment 0ms)
That sample output is from the first data-layer tests only. The suite now covers helpers, Electron modules, hooks, RTL component tests, and Playwright browser tests (inventory + interactions); run npm test -- --run for the Vitest count and npm run test:e2e:ci for Playwright. Continuous integration runs npm run lint, npm run test:coverage (minimum thresholds in vitest.config.ts), npm run build, and npm run test:e2e:ci — see .github/workflows/test.yml.
Branch protection is how this upstream repo can match the workflow the app teaches: after you enable it, routine work should show up as PRs merged into main, not only as linear commits from direct pushes.
Now that CI exists, protect the main branch so that:
- Every change must come through a Pull Request
- Required status checks (from CI: lint, tests with coverage, build, Playwright browser tests) must pass before merging is allowed
Recommended: configure a branch ruleset on main (required PRs, required checks, block force-push). This repo includes a copy-paste checklist in .github/RULESET_MAIN.md, aligned with GitHub’s About rulesets. Rulesets layer with classic protection and are easy for students to read without admin access.
Alternative — classic branch protection (with official documentation links):
-
Go to your repository → Settings → Branches → Branch protection rules → Add rule for the branch
main. -
Enable these two settings (these are GitHub's recommended defaults for protected branches):
-
Require a pull request before merging
→ Documentation: About protected branches -
Require status checks to pass before merging
→ SelectLint, tests, coverage, web + electron buildandPlaywright (inventory + interactions)(jobname:values from .github/workflows/test.yml; GitHub may prefix them with the workflow name, e.g.Test / …)
→ Documentation: Require status checks before merging
-
-
Save the rule.
After this is enabled, the default path for code to reach main is a reviewed, passing Pull Request — the same GitHub Flow the app teaches. (Org admins may still bypass in emergencies; the goal is day-to-day PR discipline.)
Why this is the documented best practice:
- It prevents broken code from landing on the stable branch.
- It creates an auditable history of every change.
- It is the same process used by professional teams and open-source projects.
References:
This project is intentionally simple and well-documented so students can:
- See how modern frontend tooling works
- Understand the difference between a web app and a desktop app
- Learn professional GitHub workflows by using the app and by reading its code
Feel free to ask questions about any part of the stack — that's the whole point of building it together.
During the implementation of the desktop (Electron) version and the "Fetch Upstream Repo Data" button, we encountered several non-obvious issues that are common when combining Vite + React + Electron. The AI coding assistant helped diagnose each one in real time using the running terminal output, DevTools console, and screenshots.
error during start dev server and electron app:
Error: Electron uninstall
at getElectronPath (…/electron-vite/dist/chunks/…)
The electron npm package did not finish downloading its OS-specific binary, so node_modules/electron/path.txt is missing or empty. That happens if lifecycle scripts were skipped (--ignore-scripts), install was interrupted, or node_modules was copied incompletely.
From the project root:
npm run electron:installOr reinstall: rm -rf node_modules && npm ci (see INSTALL.md). postinstall runs the same ensure step automatically on a normal install.
Unable to load preload script: .../preload.mjs
SyntaxError: Cannot use import statement outside a module
sandbox: true (the strongest isolation setting) is incompatible with ESM preload scripts produced by electron-vite.
Changed sandbox: true → sandbox: false in createWindow() while keeping contextIsolation: true and nodeIntegration: false. This is the documented secure configuration when using ESM preload.
Electron Security Warning (Insecure Content-Security-Policy)
This renderer process has either no Content Security Policy set
or a policy with "unsafe-eval" enabled.
A <meta http-equiv="Content-Security-Policy"> tag in index.html is often ignored or overridden when Electron loads the page via loadURL.
Injected the CSP header programmatically from the main process using:
mainWindow.webContents.session.webRequest.onHeadersReceived(
(details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
"Content-Security-Policy": [
"script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:5173 ws://localhost:5173;",
],
},
});
},
);This is the officially recommended approach in the Electron security documentation.
- Terminal shows
[Main] did-finish-load - DevTools shows only the preload log and CSP warning
- No React components, no
[Renderer]logs,#rootremains empty
Multiple interacting issues:
- electron-vite defaults the renderer Vite
rootto./src/renderer. This project keepsindex.htmlat the repository root, so the dev server was serving the wrong tree — diagnostics showedReact entry script tag found in DOM: falsebecause the loaded HTML was not the real app shell. - The dev server can still be racing Electron on first connect.
- Missing or misconfigured tooling made renderer errors hard to see.
- Set
renderer.rootto the project root androllupOptions.inputto an absoluteindex.htmlinelectron.vite.config.ts; alignedserver.port/strictPortwithvite.config.ts. - Kept
vite-plugin-electron-renderer(renderer()plugin) for Electron-friendly renderer bundling. - Main process follows the Electron
appAPI:app.whenReady()for startup, dev server only whenNODE_ENV === 'development'orVITE_DEV_SERVER_URLis set (unpackagedelectron .after build usesloadFile, not localhost), CSP onsession.defaultSession, andresolveProductionIndexHtml()that resolves../../out/renderer/index.htmlfromdist-electron/main(see Problem 5). - Dev retries: if the DOM still lacks the React entry script or
did-fail-loadfires, reload the dev URL up to three times (neverloadFileofindex.htmlin dev —file://cannot resolve/src/main.tsx). - Startup logging,
console-messageforwarding,did-fail-load, andrender-process-gonehandlers for visibility.
[Main] VITE_DEV_SERVER_URL: undefined
[Main] VITE_DEV_SERVER_URL not set, falling back to http://localhost:5173
electron-vite only injects this environment variable under certain conditions. The fallback to a hardcoded port works, but a small startup delay is still required.
- After
npm run electron:build, opening the app shows an empty white window (no header, no React UI).
- Wrong path to
index.html: The main bundle lives atdist-electron/main/main.js. A relative path../out/renderer/index.htmlpoints underdist-electron/out/..., which does not exist. The electron-vite renderer output is at the project rootout/renderer/, so the correct relative path is../../out/renderer/index.html. - Treating every unpackaged run as “dev”: Using
isDev = !app.isPackagedmade a post-buildelectron .try to loadhttp://localhost:5173even when the Vite dev server was off. Production-style runs must useloadFilewith the builtout/renderer/index.html.
resolveProductionIndexHtml()tries../../out/renderer/index.htmlfirst, then other candidates.shouldLoadDevServer()istrueonly when unpackaged and (NODE_ENV === 'development'orVITE_DEV_SERVER_URLis set).- Run a local production smoke test after build:
npm run electron:preview(setsNODE_ENV=productionbeforeelectron .). Packaged apps useapp.isPackaged === trueand always load from disk.
After applying the fixes above, the desktop app successfully renders the full React UI, including the new "Fetch Upstream Repo Data" button (visible only when window.electronAPI exists). The button triggers a secure git clone / git pull of the private upstream course repository and refreshes the dynamic week/day selector.
All changes were made iteratively with the AI assistant reviewing terminal output, DevTools console, and screenshots in real time — exactly the workflow this project encourages students to adopt.
Built with ❤️ for Code Platoon AI DevOps cohort
Last updated: May 9, 2026