Skip to content

Commit 55f2bf6

Browse files
committed
feat: standardize build tooling and quality gates (ADR-016)
- Add reusable CI scripts in tool/ci/ (analyze budget, coverage check) - Add make check target for quick validation (analyze + test, no builds) - Add make release V=patch|minor|major for automated versioning - Add make icons-check to validate icon artifacts - Add TTY detection for output suppression in non-interactive mode - Refactor CI workflow: rename test->quality, use reusable scripts - Add cross-platform CLI smoke test workflow (.github/workflows/smoke.yml) - Add token sanitization in LoggerService for secure log exports - Add AMOLED pure black mode setting for OLED screens - Add tab persistence (last active tab saved/restored) - Add tab preloading via IndexedStack (all tabs stay alive) - Add slow and requires_network test tags to dart_test.yaml - Add test/support/fixture_reader.dart for centralized fixture loading - Add ai-docs/ directory for external API documentation - Archive completed epics to ROADMAP.archive.done.md - Bump version to 1.14.0+23
1 parent 44e6285 commit 55f2bf6

File tree

17 files changed

+624
-206
lines changed

17 files changed

+624
-206
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
branches: [main]
88

99
jobs:
10-
test:
10+
quality:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- uses: actions/checkout@v4
@@ -30,40 +30,14 @@ jobs:
3030
- name: Install dependencies
3131
run: flutter pub get
3232

33-
- name: Analyze code
34-
run: |
35-
flutter analyze --no-fatal-infos --no-fatal-warnings || {
36-
# If only info issues, exit 0
37-
flutter analyze --no-fatal-infos --no-fatal-warnings 2>&1 | grep -q "error •" && exit 1
38-
exit 0
39-
}
33+
- name: Analyze code (budget gate)
34+
run: bash tool/ci/check_analyze_budget.sh 100
4035

4136
- name: Run tests with coverage
4237
run: flutter test --coverage --exclude-tags=hardware
4338

4439
- name: Check coverage threshold
45-
run: |
46-
# Install lcov if not available
47-
if ! command -v lcov &> /dev/null; then
48-
sudo apt-get install -y lcov
49-
fi
50-
51-
# Remove generated files and UI dialogs from coverage
52-
lcov --remove coverage/lcov.info 'lib/l10n/*' 'lib/ui/dialogs/*' 'lib/core/paths/*' 'lib/models/*.g.dart' '**/generated_plugin_registrant.dart' -o coverage/lcov.info --ignore-errors unused
53-
54-
# Calculate coverage percentage
55-
COVERAGE=$(lcov --summary coverage/lcov.info 2>/dev/null | grep "lines" | grep -oP '\d+\.\d+(?=%)')
56-
57-
echo "Coverage: ${COVERAGE}%"
58-
59-
# Check if coverage meets threshold (40% - accounts for CI environment variance)
60-
# Note: Local target is 60%, CI runs without hardware tests so threshold is lower
61-
if (( $(echo "$COVERAGE < 40" | bc -l) )); then
62-
echo "::error::Coverage ${COVERAGE}% is below minimum threshold of 40%"
63-
exit 1
64-
fi
65-
66-
echo "Coverage ${COVERAGE}% meets threshold of 40%"
40+
run: bash tool/ci/check_coverage.sh 40
6741

6842
- name: Upload coverage to Codecov
6943
uses: codecov/codecov-action@v4
@@ -73,125 +47,9 @@ jobs:
7347
fail_ci_if_error: false
7448
verbose: true
7549

76-
build-linux:
77-
if: false # Temporarily disabled
78-
runs-on: ubuntu-latest
79-
steps:
80-
- uses: actions/checkout@v4
81-
82-
- name: Cache Pub dependencies
83-
uses: actions/cache@v4
84-
with:
85-
path: ${{ runner.os == 'Windows' && '~\AppData\Local\Pub\Cache' || '~/.pub-cache' }}
86-
key: ${{ runner.os }}-pub-cache-${{ hashFiles('**/pubspec.lock') }}
87-
restore-keys: |
88-
${{ runner.os }}-pub-cache-
89-
90-
- name: Setup Flutter
91-
uses: subosito/flutter-action@v2
92-
with:
93-
flutter-version: "3.38.3"
94-
channel: "stable"
95-
cache: true
96-
97-
- name: Install Linux dependencies
98-
run: |
99-
sudo apt-get update
100-
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev libsecret-1-dev libayatana-appindicator3-dev libkeybinder-3.0-dev
101-
102-
- name: Build Linux GUI
103-
run: flutter build linux --release
104-
105-
- name: Rename GUI binary
106-
run: mv build/linux/x64/release/bundle/crossbar build/linux/x64/release/bundle/crossbar-gui
107-
108-
- name: Build unified CLI (includes launcher)
109-
run: dart compile exe bin/crossbar.dart -o build/linux/x64/release/bundle/crossbar
110-
111-
- name: Upload Linux artifact
112-
uses: actions/upload-artifact@v4
113-
with:
114-
name: crossbar-linux
115-
path: build/linux/x64/release/bundle/
116-
retention-days: 7
117-
118-
build-macos:
119-
if: false # Temporarily disabled
120-
runs-on: macos-latest
121-
steps:
122-
- uses: actions/checkout@v4
123-
124-
- name: Cache Pub dependencies
125-
uses: actions/cache@v4
126-
with:
127-
path: ${{ runner.os == 'Windows' && '~\AppData\Local\Pub\Cache' || '~/.pub-cache' }}
128-
key: ${{ runner.os }}-pub-cache-${{ hashFiles('**/pubspec.lock') }}
129-
restore-keys: |
130-
${{ runner.os }}-pub-cache-
131-
132-
- name: Setup Flutter
133-
uses: subosito/flutter-action@v2
134-
with:
135-
flutter-version: "3.38.3"
136-
channel: "stable"
137-
cache: true
138-
139-
- name: Build macOS GUI
140-
run: flutter build macos --release
141-
142-
- name: Rename GUI binary and add CLI
143-
run: |
144-
cd build/macos/Build/Products/Release/crossbar.app/Contents/MacOS
145-
mv crossbar crossbar-gui
146-
dart compile exe $GITHUB_WORKSPACE/bin/crossbar.dart -o crossbar
147-
148-
- name: Upload macOS artifact
149-
uses: actions/upload-artifact@v4
150-
with:
151-
name: crossbar-macos
152-
path: build/macos/Build/Products/Release/
153-
retention-days: 7
154-
155-
build-windows:
156-
if: false # Temporarily disabled
157-
runs-on: windows-latest
158-
steps:
159-
- uses: actions/checkout@v4
160-
161-
- name: Cache Pub dependencies
162-
uses: actions/cache@v4
163-
with:
164-
path: ${{ runner.os == 'Windows' && '~\AppData\Local\Pub\Cache' || '~/.pub-cache' }}
165-
key: ${{ runner.os }}-pub-cache-${{ hashFiles('**/pubspec.lock') }}
166-
restore-keys: |
167-
${{ runner.os }}-pub-cache-
168-
169-
- name: Setup Flutter
170-
uses: subosito/flutter-action@v2
171-
with:
172-
flutter-version: "3.38.3"
173-
channel: "stable"
174-
cache: true
175-
176-
- name: Build Windows GUI
177-
run: flutter build windows --release
178-
179-
- name: Rename GUI binary
180-
run: Rename-Item -Path "build/windows/x64/runner/Release/crossbar.exe" -NewName "crossbar-gui.exe"
181-
182-
- name: Build unified CLI (includes launcher)
183-
run: dart compile exe bin/crossbar.dart -o build/windows/x64/runner/Release/crossbar.exe
184-
185-
- name: Upload Windows artifact
186-
uses: actions/upload-artifact@v4
187-
with:
188-
name: crossbar-windows
189-
path: build/windows/x64/runner/Release/
190-
retention-days: 7
191-
19250
build-android:
19351
runs-on: ubuntu-latest
194-
# Runs in parallel with tests (no 'needs: test')
52+
# Runs in parallel with quality (no 'needs: quality')
19553
steps:
19654
- uses: actions/checkout@v4
19755

@@ -262,17 +120,17 @@ jobs:
262120
path: build/app/outputs/flutter-apk/app-release.apk
263121
retention-days: 7
264122

265-
# Final check: ensures workflow fails if tests failed
266-
# Builds run to completion regardless of test status
123+
# Final check: ensures workflow fails if quality failed
124+
# Builds run to completion regardless of quality status
267125
ci-status:
268126
runs-on: ubuntu-latest
269-
needs: [test, build-android] # linux/macos/windows temporarily disabled
127+
needs: [quality, build-android]
270128
if: always()
271129
steps:
272-
- name: Check test status
130+
- name: Check quality status
273131
run: |
274-
if [ "${{ needs.test.result }}" != "success" ]; then
275-
echo "::error::Tests failed! Result: ${{ needs.test.result }}"
132+
if [ "${{ needs.quality.result }}" != "success" ]; then
133+
echo "::error::Quality checks failed! Result: ${{ needs.quality.result }}"
276134
exit 1
277135
fi
278-
echo "All tests passed and builds completed successfully!"
136+
echo "All quality checks passed and builds completed successfully!"

.github/workflows/smoke.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Smoke Test
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*.0.0" # Only on minor/major releases
7+
8+
jobs:
9+
cli-smoke:
10+
strategy:
11+
matrix:
12+
os: [ubuntu-latest, macos-latest, windows-latest]
13+
runs-on: ${{ matrix.os }}
14+
timeout-minutes: 15
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Setup Flutter
19+
uses: subosito/flutter-action@v2
20+
with:
21+
flutter-version: "3.38.3"
22+
channel: "stable"
23+
cache: true
24+
25+
- name: Install Linux dependencies
26+
if: runner.os == 'Linux'
27+
run: |
28+
sudo apt-get update
29+
sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev libsecret-1-dev libayatana-appindicator3-dev libkeybinder-3.0-dev
30+
31+
- name: Get dependencies
32+
run: |
33+
flutter pub get
34+
cd packages/crossbar_cli && dart pub get
35+
36+
- name: Compile CLI
37+
run: cd packages/crossbar_cli && dart compile exe bin/crossbar.dart -o ../../crossbar-smoke
38+
39+
- name: CLI smoke tests
40+
shell: bash
41+
run: |
42+
echo "=== Version ==="
43+
./crossbar-smoke --version
44+
45+
echo "=== Help ==="
46+
./crossbar-smoke --help
47+
48+
echo "=== CPU ==="
49+
./crossbar-smoke --cpu || echo "CPU command returned non-zero (expected on CI)"
50+
51+
echo "=== Memory ==="
52+
./crossbar-smoke --memory || echo "Memory command returned non-zero (expected on CI)"
53+
54+
echo "=== Uptime ==="
55+
./crossbar-smoke --uptime || echo "Uptime command returned non-zero (expected on CI)"
56+
57+
echo "=== All smoke tests passed ==="

AGENTS.md

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Quando terminar as tarefas solicitadas faça as seguintes etapas:
8080
## 2. Identidade do Projeto
8181

8282
- **Nome**: Crossbar (Universal Plugin System)
83-
- **Versão Atual**: `1.13.0+22` (atualize ao final de cada sessão).
83+
- **Versão Atual**: `1.14.0+23` (atualize ao final de cada sessão).
8484
- **Stack**: Flutter `3.38.3` (CI), Dart `3.10+`.
8585
- **Objetivo**: Sistema de plugins compatível com BitBar/Argos para Linux, Windows, macOS, Android e iOS.
8686
- **Status**: Estável (v1.0+). Todas as fases do plano original concluídas.
@@ -216,12 +216,15 @@ Plugins usam a própria CLI do Crossbar para obter dados.
216216

217217
| Ação | Comando |
218218
| ----------------------- | ------------------------------------------------------------------------- |
219+
| Validação Rápida | `make check` (analyze + test, sem builds) |
219220
| Rodar Testes | `flutter test --coverage` |
220221
| Testes (sem hardware) | `flutter test --exclude-tags=hardware` (evita glitches locais) |
221-
| Verificar Coverage | Verificar se coverage está >= 35% (lcov --summary coverage/lcov.info) |
222+
| Verificar Coverage | `bash tool/ci/check_coverage.sh 35` |
222223
| Build Release (Linux) | `make linux` |
223224
| Build Release (Android) | `make android` ou `make android CAPTION="- Feature 1\n- Fix 2"` |
224-
| Analisar Código | `flutter analyze --no-fatal-infos` |
225+
| Analisar Código | `make analyze` (com budget gate de 100 issues) |
226+
| Verificar Ícones | `make icons-check` |
227+
| Release Automático | `make release V=patch\|minor\|major` |
225228
| Monitorar CI | `gh run watch` |
226229
| **Matar GUI + Reabrir** | `pkill -9 -f crossbar-gui; ./build/linux/x64/release/bundle/crossbar gui` |
227230

@@ -699,6 +702,39 @@ SchedulerService._onPluginOutput()
699702
- ⚠️ `WidgetMenuActivity` constrói UI programaticamente (sem XML layout) — mais flexível mas menos padrão
700703
- ℹ️ Dark mode detectado via `Configuration.uiMode`
701704
705+
### ADR-016: Build Tooling & Quality Gates Standardization (2026-02-15)
706+
707+
**Status**: ✅ Accepted
708+
**Context**: Build tooling and quality gates were inconsistent between local development and CI. Coverage checking was inline in the Makefile, analyzer just used `--no-fatal-infos` without issue budget, and there was no quick validation target separate from the full `precommit` sequence. Patterns from sibling projects (codewalk, dash-for-cf) demonstrated better practices: reusable CI scripts, analyzer budgets, `make check` for fast feedback, `make release` for automated versioning, TTY-aware output suppression, and tab persistence in the UI.
709+
710+
**Decision**:
711+
1. **Reusable CI scripts** in `tool/ci/`: `check_analyze_budget.sh` (max 100 issues) and `check_coverage.sh` (configurable threshold)
712+
2. **`make check`**: Quick validation (analyze + test, no builds) for fast developer feedback
713+
3. **`make release V=patch|minor|major`**: Automated version bump, commit, tag, and push
714+
4. **`make icons-check`**: Validate icon artifacts exist after generation
715+
5. **TTY detection**: Suppress verbose build output in non-interactive mode (CI/agents)
716+
6. **Analyzer budget**: Cap info+warning issues at 100 to prevent quality degradation
717+
7. **CI workflow refactor**: Renamed `test` job to `quality`, uses reusable scripts
718+
8. **Smoke test workflow**: Cross-platform CLI validation on major releases
719+
9. **Token sanitization**: `LoggerService.sanitize()` redacts potential secrets in log exports
720+
10. **AMOLED black mode**: Pure black (#000000) theme for OLED screens via `amoledBlack` setting
721+
11. **Tab persistence**: Last active tab saved/restored via `lastTabIndex` in SharedPreferences
722+
12. **Tab preloading**: `IndexedStack` keeps all tabs alive for instant switching
723+
13. **Test tags**: Added `slow` and `requires_network` tags to `dart_test.yaml`
724+
14. **Fixture reader**: `test/support/fixture_reader.dart` for centralized test fixture loading
725+
15. **ROADMAP archive**: Completed epics moved to `ROADMAP.archive.done.md`
726+
16. **ai-docs/**: Directory for external API documentation reference
727+
728+
**Consequences**:
729+
- ✅ Consistent quality gates between local and CI environments
730+
- ✅ Faster developer feedback via `make check` (~30s vs ~5min for `precommit`)
731+
- ✅ Automated releases reduce human error in version management
732+
- ✅ Secret leak prevention in exported logs
733+
- ✅ Better battery life on AMOLED screens in dark mode
734+
- ✅ Smoother tab switching with preloaded content
735+
- ⚠️ IndexedStack uses more memory (all 3 tabs rendered simultaneously)
736+
- ⚠️ Analyzer budget needs periodic adjustment as codebase grows
737+
702738
### Template para Novas ADRs
703739
704740
```markdown

0 commit comments

Comments
 (0)