Skip to content

Commit 11d77a0

Browse files
committed
Merge remote-tracking branch 'upstream/master' into issue-70-explode-files
2 parents 8599f9a + e09dc35 commit 11d77a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+1401
-1069
lines changed

.github/workflows/e2e-tests.yaml

+7-30
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,15 @@
11
name: Lint & E2E Checks
22
on:
3-
workflow_dispatch:
4-
issue_comment:
5-
types: [created]
3+
pull_request:
64

75
env:
86
docker_registry: ghcr.io/joshzcold
97
game_store: memory
108

119
jobs:
12-
check-comment:
13-
name: Check for Maintainer Comment
14-
runs-on: ubuntu-latest
15-
outputs:
16-
should_run: ${{ steps.find-comment.outputs.comment-id != '' }}
17-
steps:
18-
- name: Find "!test" Comment by Maintainers
19-
id: find-comment
20-
uses: peter-evans/find-comment@v3
21-
with:
22-
token: ${{ secrets.GITHUB_TOKEN }}
23-
issue-number: ${{ github.event.issue.number }}
24-
comment-author: joshzcold
25-
body-includes: "!test"
26-
- name: Set should_run to true for manual dispatch
27-
id: set-run
28-
if: ${{ github.event_name == 'workflow_dispatch' }}
29-
run: echo "should_run=true" >> $GITHUB_OUTPUT
30-
- name: Set should_run based on comment
31-
if: ${{ github.event_name == 'issue_comment' }}
32-
run: echo "should_run=${{ steps.find-comment.outputs.comment-id != '' }}" >> $GITHUB_OUTPUT
33-
3410
lint:
3511
name: Lint Check
3612
runs-on: ubuntu-latest
37-
needs: [check-comment]
38-
if: ${{ github.event_name == 'workflow_dispatch' || needs.check-comment.outputs.should_run == 'true' }}
3913
steps:
4014
- uses: actions/checkout@v4
4115
- name: Set up Node.js
@@ -50,8 +24,6 @@ jobs:
5024

5125
test:
5226
name: E2E Tests
53-
needs: [lint, check-comment]
54-
if: ${{ github.event_name == 'workflow_dispatch' || needs.check-comment.outputs.should_run == 'true' }}
5527
runs-on: ubuntu-latest
5628
steps:
5729
- uses: actions/checkout@v4
@@ -64,11 +36,16 @@ jobs:
6436
run: |
6537
npm ci
6638
cd e2e
67-
npm ci
6839
- name: Install Playwright browsers
6940
run: |
7041
cd e2e
7142
npx playwright install --with-deps
7243
- name: Run e2e tests
7344
run: |
7445
make e2e
46+
- name: Publish Test Results
47+
uses: EnricoMi/publish-unit-test-result-action@v2
48+
if: always()
49+
with:
50+
files: |
51+
e2e/test-results/*.xml

Makefile

-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ dev-down:
6464
e2e: dev-background
6565
npm install
6666
cd e2e
67-
npm install
6867
npx playwright test
6968
cd -
7069
$(MAKE) dev-down

doc/development.md

+291
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# Doing Development on Friendly Feud
2+
3+
## Table of Contents
4+
1. [Project Structure](#project-structure)
5+
2. [Dependencies](#dependencies)
6+
3. [Setup Instructions](#setup-instructions)
7+
4. [Quick Start](#quick-start)
8+
5. [Running Development](#running-development)
9+
6. [End-to-End Testing](#end-to-end-testing)
10+
7. [Frontend Overview](#frontend-overview)
11+
8. [Backend Overview](#backend-overview)
12+
9. [Troubleshooting](#troubleshooting)
13+
10. [Contributing Guidelines](#contributing-guidelines)
14+
15+
## Project Structure
16+
```plaintext
17+
├── backend/ # Golang backend
18+
│ ├── api/ # Backend API and websocket logic
19+
│ ├── Dockerfile # Backend Dockerfile
20+
│ ├── main.go # Entry point for backend server
21+
├── docker/ # Docker and nginx configuration files
22+
│ ├── allinone/ # All-in-one Docker configuration
23+
│ ├── nginx/ # Nginx configuration
24+
│ └── docker-compose*.yaml # Docker compose files
25+
├── doc/ # Documentation and development guide
26+
├── e2e/ # End-to-end tests using Playwright
27+
├── games/ # Pre-built game files in JSON format
28+
├── i18n/ # Localization and translation files
29+
├── public/ # Static assets (images, fonts, etc.)
30+
├── scripts/ # Utility scripts for game creation
31+
├── Dockerfile # Frontend dockerfile
32+
├── Dockerfile.allinone # All-in-one dockerfile
33+
├── src/ # Next.js frontend
34+
│ ├── components/ # React components
35+
│ ├── lib/ # Utility functions
36+
│ ├── pages/ # Next.js page components
37+
```
38+
39+
40+
## Dependencies
41+
42+
### System Requirements
43+
44+
- [Docker](https://docs.docker.com/engine/install/)
45+
- [Docker Compose](https://docs.docker.com/compose/install/)
46+
- [Node.js](https://nodejs.org/) (version specified in `.nvmrc`)
47+
- [Go](https://go.dev/doc/install)
48+
- [Make](https://www.gnu.org/software/make/)
49+
50+
> Note: Required versions are not updated, but newest versions should work 😅
51+
52+
## Setup Instructions
53+
54+
### Windows Setup
55+
56+
For Windows users, we recommend using WSL.
57+
58+
You might need to configure Windows firewall to allow WSL network access:
59+
```powershell
60+
# Add outbound rules
61+
netsh advfirewall firewall add rule name="WSL2 HTTPS Out" dir=out action=allow protocol=TCP localport=443
62+
netsh advfirewall firewall add rule name="WSL2 HTTP Out" dir=out action=allow protocol=TCP localport=80
63+
# Add inbound rules
64+
netsh advfirewall firewall add rule name="WSL2 HTTPS" dir=in action=allow protocol=TCP localport=443
65+
netsh advfirewall firewall add rule name="WSL2 HTTP" dir=in action=allow protocol=TCP localport=80
66+
```
67+
68+
### Linux Setup
69+
70+
Install dependencies
71+
72+
## Quick Start
73+
1. Install dependencies
74+
2. Clone the repository
75+
3. Start development environment:
76+
```bash
77+
make dev
78+
```
79+
4. Access the application at [localhost](https://localhost/)
80+
81+
## Running development
82+
The stack consists of:
83+
84+
- `frontend`: Next.js
85+
- `backend`: Golang
86+
- `proxy`: Nginx as the entry point
87+
88+
The development environment is managed through a Makefile. Key commands include:
89+
90+
- `make dev`: Builds and starts the development stack
91+
- `make dev-background`: Same as `make dev`, but detaches
92+
- `make dev-down`: Stops/removes the development stack
93+
94+
Access the application at [localhost/](https://localhost/)
95+
96+
The compose files should automatically be selected by the Makefile, but you can:
97+
- check out the [Linux version](../docker/docker-compose.yaml) if on Linux or Macos
98+
- check out the [WSL version](../docker/docker-compose-dev-wsl.yaml) if on Windows
99+
100+
## End-to-End Testing
101+
102+
`make e2e-ui` will launch [playwright](https://playwright.dev/)
103+
104+
The e2e tests are located in the [e2e](../e2e/) folder.
105+
106+
Tests are marked with their `*.spec.js` file name
107+
108+
## Frontend Overview
109+
The frontend is using `Next.js` as its way to serve pages.
110+
111+
[Next.js Project Structure](https://nextjs.org/docs/app/getting-started/project-structure)
112+
113+
Code is arranged in the [./src](../src/) folder with [./src/pages/index.jsx](../src/pages/index.jsx) being the entry point
114+
115+
From there you can expect the usual React functionality.
116+
117+
The `frontend` connects back to the `backend` via the `nginx` proxy to setup a WebSocket connection that will control its behavior when data comes in.
118+
119+
We store a cookie to keep the user's session in the game as they refresh the page.
120+
121+
### Working with styles
122+
123+
You can use anything from [TailwindCSS](https://tailwindcss.com/) as long as the colors you use match the colors found in [tailwind.config.js](../tailwind.config.js)
124+
125+
```js
126+
// ....
127+
success: {
128+
900: "#14532D",
129+
700: "#15803D",
130+
500: "#22C55E",
131+
300: "#86EFAC",
132+
200: "#BBF7D0",
133+
},
134+
secondary: {
135+
900: "#A1A1AA",
136+
700: "#D4D4D8",
137+
500: "#E4E4E7",
138+
300: "#F4F4F5",
139+
200: "#FAFAFA",
140+
},
141+
// ....
142+
```
143+
144+
This looks like
145+
146+
```html
147+
<div className="rounded bg-success-200 p-2">{t("Answer")} 1</div>
148+
<div className="rounded bg-primary-200 p-2">{t("points")} 1</div>
149+
```
150+
151+
What this does is setup a "Theme" we use for the theme picker for the game, so make sure you use the colors named in that configuration file.
152+
153+
## Backend Overview
154+
155+
The backend is a `Golang` application located in [./backend](../backend/).
156+
157+
The entry point is `main.go`, where we start our WebSocket server:
158+
159+
```go
160+
http.HandleFunc("/api/ws", func(httpWriter http.ResponseWriter, httpRequest *http.Request) {
161+
api.ServeWs(httpWriter, httpRequest)
162+
})
163+
```
164+
165+
We also set up a "store" that backend functions interact with to store game data:
166+
167+
```go
168+
err := api.NewGameStore(cfg.store)
169+
170+
func NewGameStore(gameStore string) error {
171+
switch gameStore {
172+
case "memory":
173+
log.Println("Starting famf with memory store")
174+
store = NewMemoryStore()
175+
return nil
176+
case "sqlite":
177+
log.Println("Starting famf with sqlite store")
178+
store, _ = NewSQLiteStore()
179+
default:
180+
return fmt.Errorf("unknown store: %q", gameStore)
181+
}
182+
return nil
183+
}
184+
```
185+
186+
The variable `store` is used by functions to read and write game state:
187+
188+
```go
189+
var store gameStore
190+
```
191+
192+
This setup creates a connection to the frontend and establishes two [goroutines](https://go.dev/tour/concurrency/1) that asynchronously read and write to the client:
193+
194+
```go
195+
go client.writePump()
196+
go client.readPump()
197+
```
198+
199+
In `readPump()`, we receive messages and pass them to `EventPipe()`.
200+
201+
`EventPipe()` is located in [backend/api/pipe.go](../backend/api/pipe.go) and serves as the next entry point for handling events from the frontend.
202+
203+
We parse messages like these in the `parseEvent()` function:
204+
205+
```json
206+
{ "action": "buzz", "room": "HL6T", "id": "fds-fds-21-fds-f-321"}
207+
{ "action": "clearbuzzers", "room": "HL6T"}
208+
```
209+
210+
```go
211+
func parseEvent(message []byte) (*Event, error) {
212+
var event *Event
213+
err := json.Unmarshal(message, &event)
214+
if err != nil {
215+
return nil, err
216+
}
217+
return event, nil
218+
}
219+
```
220+
221+
If the action exists in the `receiveActions` map, we call the corresponding function.
222+
223+
Backend functions typically follow this pattern:
224+
225+
1. Retrieve data from the store for the specified room
226+
2. Perform actions on the data
227+
3. Send updated data to the player or the entire room
228+
4. Write changes back to the store
229+
230+
When you see code like this:
231+
232+
```go
233+
client.send <- message
234+
```
235+
236+
or
237+
238+
```go
239+
if room.Hub.broadcast != nil {
240+
room.Hub.broadcast <- message
241+
}
242+
if room.Hub.stop != nil {
243+
room.Hub.stop <- true
244+
}
245+
```
246+
247+
We're sending data back to goroutines initialized when the player connects or when a [Hub](../backend/api/hub.go) is created for the room.
248+
249+
### Writing a new Store
250+
251+
Creating a new game store is straightforward.
252+
253+
The Go interface in [backend/api/store.go](../backend/api/store.go) defines the required functions:
254+
255+
```go
256+
type gameStore interface {}
257+
```
258+
259+
For a simple example, see the memory store in [backend/api/store-memory.go](../backend/api/store-memory.go), which uses a `map` to store game data.
260+
261+
> Note: Production deployments currently use the `sqlite` store.
262+
263+
When you see:
264+
265+
```go
266+
m.mu.RLock()
267+
defer m.mu.RUnlock()
268+
```
269+
270+
This lock prevents race conditions when accessing memory in asynchronous goroutines.
271+
272+
273+
## Troubleshooting
274+
275+
1. If localhost doesn't work, try using `127.0.0.1` instead. On Windows with WSL, verify with `curl localhost`
276+
2. For WebSocket issues:
277+
- Verify the backend is running
278+
3. If node_modules aren't updating:
279+
```sh
280+
make dev-down
281+
make dev
282+
```
283+
284+
## Contributing Guidelines
285+
We welcome contributions! Please follow these guidelines:
286+
1. Fork the repository and create your branch from `master`
287+
2. Follow the existing code style and architecture
288+
3. Write commit messages using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
289+
4. Add tests for new features
290+
5. Update documentation when making changes
291+
6. Open a pull request with a detailed description

e2e/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ node_modules/
33
/playwright-report/
44
/blob-report/
55
/playwright/.cache/
6+
tmp/

0 commit comments

Comments
 (0)