-
Notifications
You must be signed in to change notification settings - Fork 19
138 lines (114 loc) · 4.49 KB
/
ci.yml
File metadata and controls
138 lines (114 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
name: CI
on:
pull_request:
branches: [main, devel]
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '24'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build SDK
run: npm run build:sdk
- name: Run tests
run: npm test
- name: Type check
run: npm run typecheck
- name: Lint SDK
run: npm run lint --workspace=@fluux/sdk
- name: Lint App
run: npm run lint --workspace=@xmpp/fluux
- name: Check for unstable Zustand selectors
run: |
# Zustand selectors passed to useStore/useSyncExternalStore MUST return
# referentially stable values. If a selector creates a new object, array,
# Set, or closure on every call, Object.is() comparison fails and React
# enters an infinite re-render loop (error #185: Maximum update depth
# exceeded).
#
# Dangerous patterns detected by this check:
#
# 1. Closure-returning selectors — a new function is created on every call:
# useRoomStore((s) => (jid) => s.rooms.has(jid)) // BAD
# Fix: use imperative store access instead:
# useCallback((jid) => roomStore.getState().rooms.has(jid), [])
#
# 2. Fallback with || [] — creates a new empty array each time:
# useIgnoreStore((s) => s.ignoredUsers[jid] || []) // BAD
# Fix: use a module-level stable reference:
# const EMPTY: T[] = []
# useIgnoreStore((s) => s.ignoredUsers[jid] ?? EMPTY)
#
# 3. Fallback with new Set()/new Map() — same issue with non-primitives:
# return get().votedPollIds.get(jid) ?? new Set() // BAD
# Fix: const EMPTY_SET = new Set<string>()
# return get().votedPollIds.get(jid) ?? EMPTY_SET
ERRORS=0
# Pattern 1: Closure-returning selectors (arrow function inside selector)
if grep -rn --include='*.ts' --include='*.tsx' \
-E 'use\w+Store\(\(s\) => \(' \
packages/ apps/ \
| grep -v 'node_modules' | grep -v '.test.' | grep -v '// '; then
echo "::error::Found closure-returning Zustand selector(s). These create a new function on every store update, causing infinite re-render loops."
ERRORS=1
fi
# Pattern 2: || [] fallback in store selectors
if grep -rn --include='*.ts' --include='*.tsx' \
-E 'use\w+Store\(\(s\) =>.*\|\| \[\]' \
packages/ apps/ \
| grep -v 'node_modules' | grep -v '.test.'; then
echo "::error::Found || [] fallback in Zustand selector(s). Use a stable empty reference (const EMPTY = []) with ?? instead."
ERRORS=1
fi
# Pattern 3: ?? new Set/Map/Array in store getters used as selectors
if grep -rn --include='*.ts' --include='*.tsx' \
-E '\?\? new (Set|Map|Array)\(\)' \
packages/fluux-sdk/src/stores/ \
| grep -v 'node_modules' | grep -v '.test.'; then
echo "::error::Found ?? new Set/Map/Array() in store code. Use a module-level EMPTY constant instead."
ERRORS=1
fi
if [ "$ERRORS" -ne 0 ]; then
exit 1
fi
echo "No unstable Zustand selector patterns found."
rust:
name: Rust
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf \
libxss-dev \
libdbus-1-dev
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
workspaces: apps/fluux/src-tauri
- name: Run tests
working-directory: apps/fluux/src-tauri
run: cargo test --locked
- name: Clippy
working-directory: apps/fluux/src-tauri
run: cargo clippy --locked -- -D warnings
- name: Check generated files are up to date
run: git diff --exit-code apps/fluux/src-tauri/gen/