|
89816aa…
|
lmata
|
1 |
# CI/CD Integration |
|
89816aa…
|
lmata
|
2 |
|
|
89816aa…
|
lmata
|
3 |
Navegador's `ci` subcommand is designed for non-interactive use in pipelines. All CI commands emit structured output and use exit codes that CI systems understand. |
|
89816aa…
|
lmata
|
4 |
|
|
89816aa…
|
lmata
|
5 |
--- |
|
89816aa…
|
lmata
|
6 |
|
|
89816aa…
|
lmata
|
7 |
## CI commands |
|
89816aa…
|
lmata
|
8 |
|
|
89816aa…
|
lmata
|
9 |
### `navegador ci ingest` |
|
89816aa…
|
lmata
|
10 |
|
|
89816aa…
|
lmata
|
11 |
Ingest the repo and output a machine-readable summary. Exits non-zero on errors. |
|
89816aa…
|
lmata
|
12 |
|
|
89816aa…
|
lmata
|
13 |
```bash |
|
89816aa…
|
lmata
|
14 |
navegador ci ingest ./src |
|
89816aa…
|
lmata
|
15 |
``` |
|
89816aa…
|
lmata
|
16 |
|
|
89816aa…
|
lmata
|
17 |
JSON output (always on in CI mode): |
|
89816aa…
|
lmata
|
18 |
|
|
89816aa…
|
lmata
|
19 |
```json |
|
89816aa…
|
lmata
|
20 |
{ |
|
89816aa…
|
lmata
|
21 |
"status": "ok", |
|
89816aa…
|
lmata
|
22 |
"nodes_created": 1240, |
|
89816aa…
|
lmata
|
23 |
"nodes_updated": 38, |
|
89816aa…
|
lmata
|
24 |
"edges_created": 4821, |
|
89816aa…
|
lmata
|
25 |
"files_processed": 87, |
|
89816aa…
|
lmata
|
26 |
"errors": [], |
|
89816aa…
|
lmata
|
27 |
"duration_seconds": 4.2 |
|
89816aa…
|
lmata
|
28 |
} |
|
89816aa…
|
lmata
|
29 |
``` |
|
89816aa…
|
lmata
|
30 |
|
|
89816aa…
|
lmata
|
31 |
### `navegador ci stats` |
|
89816aa…
|
lmata
|
32 |
|
|
89816aa…
|
lmata
|
33 |
Print graph statistics as JSON. Use to track graph growth over time or assert a minimum coverage threshold. |
|
89816aa…
|
lmata
|
34 |
|
|
89816aa…
|
lmata
|
35 |
```bash |
|
89816aa…
|
lmata
|
36 |
navegador ci stats |
|
89816aa…
|
lmata
|
37 |
``` |
|
89816aa…
|
lmata
|
38 |
|
|
89816aa…
|
lmata
|
39 |
```json |
|
89816aa…
|
lmata
|
40 |
{ |
|
89816aa…
|
lmata
|
41 |
"repositories": 1, |
|
89816aa…
|
lmata
|
42 |
"files": 87, |
|
89816aa…
|
lmata
|
43 |
"classes": 143, |
|
89816aa…
|
lmata
|
44 |
"functions": 891, |
|
89816aa…
|
lmata
|
45 |
"methods": 412, |
|
89816aa…
|
lmata
|
46 |
"concepts": 14, |
|
89816aa…
|
lmata
|
47 |
"rules": 9, |
|
89816aa…
|
lmata
|
48 |
"decisions": 6, |
|
89816aa…
|
lmata
|
49 |
"total_edges": 4821 |
|
89816aa…
|
lmata
|
50 |
} |
|
89816aa…
|
lmata
|
51 |
``` |
|
89816aa…
|
lmata
|
52 |
|
|
89816aa…
|
lmata
|
53 |
### `navegador ci check` |
|
89816aa…
|
lmata
|
54 |
|
|
89816aa…
|
lmata
|
55 |
Run assertion checks against the graph. Exits non-zero if any check fails. |
|
89816aa…
|
lmata
|
56 |
|
|
89816aa…
|
lmata
|
57 |
```bash |
|
89816aa…
|
lmata
|
58 |
navegador ci check |
|
89816aa…
|
lmata
|
59 |
``` |
|
89816aa…
|
lmata
|
60 |
|
|
89816aa…
|
lmata
|
61 |
Checks run by default: |
|
89816aa…
|
lmata
|
62 |
|
|
89816aa…
|
lmata
|
63 |
| Check | Condition for failure | |
|
89816aa…
|
lmata
|
64 |
|---|---| |
|
89816aa…
|
lmata
|
65 |
| `no-cycles` | Circular import chains detected | |
|
89816aa…
|
lmata
|
66 |
| `min-coverage` | Functions with no tests below threshold | |
|
89816aa…
|
lmata
|
67 |
| `critical-rules` | Code violates a `critical`-severity rule | |
|
89816aa…
|
lmata
|
68 |
| `dead-code` | High-confidence dead code above threshold | |
|
89816aa…
|
lmata
|
69 |
|
|
89816aa…
|
lmata
|
70 |
Configure checks in `navegador.toml`: |
|
89816aa…
|
lmata
|
71 |
|
|
89816aa…
|
lmata
|
72 |
```toml |
|
89816aa…
|
lmata
|
73 |
[ci.checks] |
|
89816aa…
|
lmata
|
74 |
no-cycles = true |
|
89816aa…
|
lmata
|
75 |
min-coverage = 60 # percent of functions with tests |
|
89816aa…
|
lmata
|
76 |
critical-rules = true |
|
89816aa…
|
lmata
|
77 |
dead-code = false # disable dead-code check |
|
89816aa…
|
lmata
|
78 |
|
|
89816aa…
|
lmata
|
79 |
[ci.thresholds] |
|
89816aa…
|
lmata
|
80 |
dead_code_max = 10 # fail if more than 10 dead-code candidates |
|
89816aa…
|
lmata
|
81 |
uncovered_max_percent = 40 # fail if more than 40% of functions lack tests |
|
89816aa…
|
lmata
|
82 |
``` |
|
89816aa…
|
lmata
|
83 |
|
|
89816aa…
|
lmata
|
84 |
### Running specific checks |
|
89816aa…
|
lmata
|
85 |
|
|
89816aa…
|
lmata
|
86 |
```bash |
|
89816aa…
|
lmata
|
87 |
navegador ci check --only no-cycles |
|
89816aa…
|
lmata
|
88 |
navegador ci check --only critical-rules,min-coverage |
|
89816aa…
|
lmata
|
89 |
navegador ci check --skip dead-code |
|
89816aa…
|
lmata
|
90 |
``` |
|
89816aa…
|
lmata
|
91 |
|
|
89816aa…
|
lmata
|
92 |
--- |
|
89816aa…
|
lmata
|
93 |
|
|
89816aa…
|
lmata
|
94 |
## Exit codes |
|
89816aa…
|
lmata
|
95 |
|
|
89816aa…
|
lmata
|
96 |
| Code | Meaning | |
|
89816aa…
|
lmata
|
97 |
|---|---| |
|
89816aa…
|
lmata
|
98 |
| `0` | Success — all checks passed | |
|
89816aa…
|
lmata
|
99 |
| `1` | Check failure — one or more assertions failed | |
|
89816aa…
|
lmata
|
100 |
| `2` | Ingest error — files could not be parsed (partial result) | |
|
89816aa…
|
lmata
|
101 |
| `3` | Configuration error — bad flags or missing config | |
|
89816aa…
|
lmata
|
102 |
| `4` | Connection error — cannot reach database or Redis | |
|
89816aa…
|
lmata
|
103 |
|
|
89816aa…
|
lmata
|
104 |
--- |
|
89816aa…
|
lmata
|
105 |
|
|
89816aa…
|
lmata
|
106 |
## GitHub Actions |
|
89816aa…
|
lmata
|
107 |
|
|
89816aa…
|
lmata
|
108 |
### Basic: ingest on push |
|
89816aa…
|
lmata
|
109 |
|
|
89816aa…
|
lmata
|
110 |
```yaml |
|
89816aa…
|
lmata
|
111 |
# .github/workflows/navegador.yml |
|
89816aa…
|
lmata
|
112 |
name: navegador |
|
89816aa…
|
lmata
|
113 |
|
|
89816aa…
|
lmata
|
114 |
on: |
|
89816aa…
|
lmata
|
115 |
push: |
|
89816aa…
|
lmata
|
116 |
branches: [main] |
|
89816aa…
|
lmata
|
117 |
pull_request: |
|
89816aa…
|
lmata
|
118 |
|
|
89816aa…
|
lmata
|
119 |
jobs: |
|
89816aa…
|
lmata
|
120 |
graph: |
|
89816aa…
|
lmata
|
121 |
runs-on: ubuntu-latest |
|
89816aa…
|
lmata
|
122 |
steps: |
|
89816aa…
|
lmata
|
123 |
- uses: actions/checkout@v4 |
|
89816aa…
|
lmata
|
124 |
|
|
89816aa…
|
lmata
|
125 |
- name: Install navegador |
|
89816aa…
|
lmata
|
126 |
run: pip install navegador |
|
89816aa…
|
lmata
|
127 |
|
|
89816aa…
|
lmata
|
128 |
- name: Ingest |
|
89816aa…
|
lmata
|
129 |
run: navegador ci ingest ./src |
|
89816aa…
|
lmata
|
130 |
|
|
89816aa…
|
lmata
|
131 |
- name: Check |
|
89816aa…
|
lmata
|
132 |
run: navegador ci check |
|
89816aa…
|
lmata
|
133 |
``` |
|
89816aa…
|
lmata
|
134 |
|
|
89816aa…
|
lmata
|
135 |
### With graph caching |
|
89816aa…
|
lmata
|
136 |
|
|
89816aa…
|
lmata
|
137 |
Cache the SQLite database between runs to speed up incremental ingestion: |
|
89816aa…
|
lmata
|
138 |
|
|
89816aa…
|
lmata
|
139 |
```yaml |
|
89816aa…
|
lmata
|
140 |
- name: Cache navegador graph |
|
89816aa…
|
lmata
|
141 |
uses: actions/cache@v4 |
|
89816aa…
|
lmata
|
142 |
with: |
|
89816aa…
|
lmata
|
143 |
path: .navegador/navegador.db |
|
89816aa…
|
lmata
|
144 |
key: navegador-${{ runner.os }}-${{ hashFiles('src/**') }} |
|
89816aa…
|
lmata
|
145 |
restore-keys: navegador-${{ runner.os }}- |
|
89816aa…
|
lmata
|
146 |
|
|
89816aa…
|
lmata
|
147 |
- name: Ingest |
|
89816aa…
|
lmata
|
148 |
run: navegador ci ingest ./src |
|
89816aa…
|
lmata
|
149 |
|
|
89816aa…
|
lmata
|
150 |
- name: Check |
|
89816aa…
|
lmata
|
151 |
run: navegador ci check |
|
89816aa…
|
lmata
|
152 |
``` |
|
89816aa…
|
lmata
|
153 |
|
|
89816aa…
|
lmata
|
154 |
### Shared graph via Redis (cluster mode) |
|
89816aa…
|
lmata
|
155 |
|
|
89816aa…
|
lmata
|
156 |
Use a shared Redis instance for team-wide graph persistence across branches and PRs: |
|
89816aa…
|
lmata
|
157 |
|
|
89816aa…
|
lmata
|
158 |
```yaml |
|
89816aa…
|
lmata
|
159 |
- name: Ingest to shared graph |
|
89816aa…
|
lmata
|
160 |
env: |
|
89816aa…
|
lmata
|
161 |
NAVEGADOR_REDIS_URL: ${{ secrets.NAVEGADOR_REDIS_URL }} |
|
89816aa…
|
lmata
|
162 |
run: | |
|
89816aa…
|
lmata
|
163 |
navegador ci ingest ./src --cluster |
|
89816aa…
|
lmata
|
164 |
navegador ci check --cluster |
|
89816aa…
|
lmata
|
165 |
``` |
|
89816aa…
|
lmata
|
166 |
|
|
89816aa…
|
lmata
|
167 |
### PR impact report |
|
89816aa…
|
lmata
|
168 |
|
|
89816aa…
|
lmata
|
169 |
Post an impact analysis comment on pull requests: |
|
89816aa…
|
lmata
|
170 |
|
|
89816aa…
|
lmata
|
171 |
```yaml |
|
89816aa…
|
lmata
|
172 |
- name: Impact analysis |
|
89816aa…
|
lmata
|
173 |
if: github.event_name == 'pull_request' |
|
89816aa…
|
lmata
|
174 |
run: | |
|
89816aa…
|
lmata
|
175 |
CHANGED=$(git diff --name-only origin/main...HEAD | grep '\.py$' | head -20) |
|
89816aa…
|
lmata
|
176 |
for f in $CHANGED; do |
|
89816aa…
|
lmata
|
177 |
navegador ci ingest "$f" |
|
89816aa…
|
lmata
|
178 |
done |
|
89816aa…
|
lmata
|
179 |
navegador impact --changed-since origin/main --format json > impact.json |
|
89816aa…
|
lmata
|
180 |
|
|
89816aa…
|
lmata
|
181 |
- name: Comment impact |
|
89816aa…
|
lmata
|
182 |
if: github.event_name == 'pull_request' |
|
89816aa…
|
lmata
|
183 |
uses: actions/github-script@v7 |
|
89816aa…
|
lmata
|
184 |
with: |
|
89816aa…
|
lmata
|
185 |
script: | |
|
89816aa…
|
lmata
|
186 |
const impact = require('./impact.json') |
|
89816aa…
|
lmata
|
187 |
const body = `## Navegador impact analysis\n\n${impact.summary}` |
|
89816aa…
|
lmata
|
188 |
github.rest.issues.createComment({ |
|
89816aa…
|
lmata
|
189 |
issue_number: context.issue.number, |
|
89816aa…
|
lmata
|
190 |
owner: context.repo.owner, |
|
89816aa…
|
lmata
|
191 |
repo: context.repo.repo, |
|
89816aa…
|
lmata
|
192 |
body |
|
89816aa…
|
lmata
|
193 |
}) |
|
89816aa…
|
lmata
|
194 |
``` |
|
89816aa…
|
lmata
|
195 |
|
|
89816aa…
|
lmata
|
196 |
--- |
|
89816aa…
|
lmata
|
197 |
|
|
89816aa…
|
lmata
|
198 |
## Editor integration |
|
89816aa…
|
lmata
|
199 |
|
|
89816aa…
|
lmata
|
200 |
### VS Code |
|
89816aa…
|
lmata
|
201 |
|
|
89816aa…
|
lmata
|
202 |
Install the [Navegador VS Code extension](https://marketplace.visualstudio.com/items?itemName=ConflictHQ.navegador) for inline context overlays and on-save re-ingest. |
|
89816aa…
|
lmata
|
203 |
|
|
89816aa…
|
lmata
|
204 |
Or configure a task in `.vscode/tasks.json` to run on save: |
|
89816aa…
|
lmata
|
205 |
|
|
89816aa…
|
lmata
|
206 |
```json |
|
89816aa…
|
lmata
|
207 |
{ |
|
89816aa…
|
lmata
|
208 |
"version": "2.0.0", |
|
89816aa…
|
lmata
|
209 |
"tasks": [ |
|
89816aa…
|
lmata
|
210 |
{ |
|
89816aa…
|
lmata
|
211 |
"label": "Navegador: re-ingest on save", |
|
89816aa…
|
lmata
|
212 |
"type": "shell", |
|
89816aa…
|
lmata
|
213 |
"command": "navegador ingest ${file}", |
|
89816aa…
|
lmata
|
214 |
"group": "build", |
|
89816aa…
|
lmata
|
215 |
"presentation": { |
|
89816aa…
|
lmata
|
216 |
"reveal": "silent", |
|
89816aa…
|
lmata
|
217 |
"panel": "shared" |
|
89816aa…
|
lmata
|
218 |
}, |
|
89816aa…
|
lmata
|
219 |
"runOptions": { |
|
89816aa…
|
lmata
|
220 |
"runOn": "folderOpen" |
|
89816aa…
|
lmata
|
221 |
} |
|
89816aa…
|
lmata
|
222 |
} |
|
89816aa…
|
lmata
|
223 |
] |
|
89816aa…
|
lmata
|
224 |
} |
|
89816aa…
|
lmata
|
225 |
``` |
|
89816aa…
|
lmata
|
226 |
|
|
89816aa…
|
lmata
|
227 |
### Neovim |
|
89816aa…
|
lmata
|
228 |
|
|
89816aa…
|
lmata
|
229 |
Add a post-write autocmd to trigger incremental ingest: |
|
89816aa…
|
lmata
|
230 |
|
|
89816aa…
|
lmata
|
231 |
```lua |
|
89816aa…
|
lmata
|
232 |
-- in your init.lua or a plugin config |
|
89816aa…
|
lmata
|
233 |
vim.api.nvim_create_autocmd("BufWritePost", { |
|
89816aa…
|
lmata
|
234 |
pattern = { "*.py", "*.ts", "*.tsx", "*.js" }, |
|
89816aa…
|
lmata
|
235 |
callback = function(ev) |
|
89816aa…
|
lmata
|
236 |
local file = ev.file |
|
89816aa…
|
lmata
|
237 |
vim.fn.jobstart({ "navegador", "ingest", file }, { detach = true }) |
|
89816aa…
|
lmata
|
238 |
end, |
|
89816aa…
|
lmata
|
239 |
}) |
|
89816aa…
|
lmata
|
240 |
``` |
|
89816aa…
|
lmata
|
241 |
|
|
89816aa…
|
lmata
|
242 |
### Pre-commit hook |
|
89816aa…
|
lmata
|
243 |
|
|
89816aa…
|
lmata
|
244 |
Run checks before committing: |
|
89816aa…
|
lmata
|
245 |
|
|
89816aa…
|
lmata
|
246 |
```yaml |
|
89816aa…
|
lmata
|
247 |
# .pre-commit-config.yaml |
|
89816aa…
|
lmata
|
248 |
repos: |
|
89816aa…
|
lmata
|
249 |
- repo: local |
|
89816aa…
|
lmata
|
250 |
hooks: |
|
89816aa…
|
lmata
|
251 |
- id: navegador-check |
|
89816aa…
|
lmata
|
252 |
name: Navegador graph checks |
|
89816aa…
|
lmata
|
253 |
entry: navegador ci check --only no-cycles,critical-rules |
|
89816aa…
|
lmata
|
254 |
language: system |
|
89816aa…
|
lmata
|
255 |
pass_filenames: false |
|
89816aa…
|
lmata
|
256 |
stages: [commit] |
|
89816aa…
|
lmata
|
257 |
``` |
|
89816aa…
|
lmata
|
258 |
|
|
89816aa…
|
lmata
|
259 |
--- |
|
89816aa…
|
lmata
|
260 |
|
|
89816aa…
|
lmata
|
261 |
## Secrets and auth |
|
89816aa…
|
lmata
|
262 |
|
|
89816aa…
|
lmata
|
263 |
The graph database path and Redis URL should come from environment variables in CI, not from committed config: |
|
89816aa…
|
lmata
|
264 |
|
|
89816aa…
|
lmata
|
265 |
```bash |
|
89816aa…
|
lmata
|
266 |
# CI environment variables |
|
89816aa…
|
lmata
|
267 |
NAVEGADOR_DB=.navegador/navegador.db # SQLite path |
|
89816aa…
|
lmata
|
268 |
NAVEGADOR_REDIS_URL=redis://... # Redis URL (cluster mode) |
|
89816aa…
|
lmata
|
269 |
GITHUB_TOKEN=ghp_... # for wiki ingestion |
|
89816aa…
|
lmata
|
270 |
``` |
|
89816aa…
|
lmata
|
271 |
|
|
89816aa…
|
lmata
|
272 |
Set these as [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions) and reference them in your workflow with `${{ secrets.NAME }}`. |