You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Verifies GitHub HMAC signature before processing
57
-
- On a qualifying `pull_request` event (opened/synchronize/reopened), creates a GitHub Check Run in `queued` state, then enqueues a BullMQ job and returns 200
57
+
- Handles two event types: `pull_request` and `workflow_run`
58
+
-**`pull_request` trigger (default):** on opened/synchronize/reopened, creates a Check Run in `queued` state, enqueues a BullMQ job, returns 200
59
+
-**`workflow_run` trigger:** on `pull_request` events, caches PR metadata in Redis (TTL 7 days) and creates a `skipped` Check Run; on `workflow_run completed` events matching the configured workflow name and conclusion, looks up cached PR metadata (falls back to GitHub API if cache is cold) then enqueues the scan
58
60
- Job ID is deduplicated by `{repo}#{pr}@{sha}` — duplicate webhook deliveries are no-ops (Redis lock + queue check)
59
61
- Exported `app` and `processWebhookRequest` for use in tests
60
62
@@ -103,26 +105,27 @@ Two separate Node.js processes:
103
105
104
106
| Module | Purpose |
105
107
|--------|---------|
106
-
|`src/config.js`| Loads and merges `config/repos.json`; cached after first read |
108
+
|`src/config.js`| Loads and merges `config/layne.json`; cached after first read |
107
109
|`src/github.js`| Check Run CRUD + label management (`ensureLabelsExist`, `setLabels`) |
108
110
|`src/metrics.js`| Prometheus metric definitions; exports no-op stubs when `METRICS_ENABLED` is not `true`|
-`trigger`: controls when scanning fires — `pull_request` (default, immediate) or `workflow_run` (deferred until a named CI workflow completes); global default → per-repo override
123
126
-`notifications` and `labels`: per-repo notifier/key wins over global; per-repo absence = inherit global entirely
124
127
-`extraArgs` fully replaces the default (not extended)
125
-
-`config/repos.json` must be present in the Docker image (`COPY config/ ./config/`)
128
+
-`config/layne.json` must be present in the Docker image (`COPY config/ ./config/`)
126
129
- Notifier contract: `async function notify({ findings, owner, repo, prNumber, toolConfig })` — must never throw
Copy file name to clipboardExpand all lines: docs/configuration.md
+88-3Lines changed: 88 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Configuration
2
2
3
-
Scanner behaviour, labels, and notifications are all configured in `config/repos.json`. Layne reads this file once at worker startup — **restart the worker to pick up changes**.
3
+
Scanner behaviour, labels, notifications, and trigger conditions are all configured in `config/layne.json`. Layne reads this file once per process startup — **restart both server and worker to pick up changes** (the automated deploy pipeline does this automatically).
files=files_from_dir("/path/to/skill-folder"), # must contain SKILL.md
120
120
betas=["skills-2025-10-02"],
121
121
)
122
-
print(skill.id) # paste this into repos.json
122
+
print(skill.id) # paste this into layne.json
123
123
```
124
124
125
125
### How args are assembled
@@ -236,7 +236,7 @@ Labels are applied **after** the Check Run is completed. Errors never affect the
236
236
237
237
### Configuration
238
238
239
-
Add a `labels` key to `$global` or to any repo entry in `config/repos.json`:
239
+
Add a `labels` key to `$global` or to any repo entry in `config/layne.json`:
240
240
241
241
```json
242
242
{
@@ -289,6 +289,91 @@ If neither `$global` nor the repo defines a `labels` key, the feature is a no-op
289
289
290
290
---
291
291
292
+
## Trigger
293
+
294
+
By default Layne scans every pull request immediately when it is opened, synchronised, or reopened. The `trigger` block lets you defer scanning until a specific CI workflow completes — useful for open-source repositories where workflows from external contributors require maintainer approval before they run.
295
+
296
+
### Modes
297
+
298
+
|`on`| Behaviour |
299
+
|---|---|
300
+
|`pull_request`|*(default)* Scan fires immediately on `opened`, `synchronize`, and `reopened` events |
301
+
|`workflow_run`| Scan fires when the named CI workflow completes with a matching conclusion |
302
+
303
+
### Schema
304
+
305
+
```json
306
+
{
307
+
"owner/repo": {
308
+
"trigger": {
309
+
"on": "workflow_run",
310
+
"workflow": "Tests Done",
311
+
"conclusions": ["success"]
312
+
}
313
+
}
314
+
}
315
+
```
316
+
317
+
| Key | Type | Default | Description |
318
+
|---|---|---|---|
319
+
|`on`|`"pull_request"`\|`"workflow_run"`|`"pull_request"`| When to trigger the scan |
320
+
|`workflow`| string | — | Name of the GitHub Actions workflow to watch. Required when `on` is `"workflow_run"`|
321
+
|`conclusions`| string[]|`["success"]`| Workflow conclusions that trigger the scan. Valid values: `success`, `failure`, `neutral`, `cancelled`, `skipped`, `timed_out`, `action_required`|
322
+
323
+
> **`trigger` can be set globally.** Set it under `$global` to apply to all repos, then override per-repo as needed.
324
+
325
+
### How `workflow_run` works
326
+
327
+
When `on: workflow_run` is configured for a repo:
328
+
329
+
1.**On `pull_request`** — Layne caches the PR metadata in Redis (7-day TTL) and creates a `skipped` Check Run so the deferral is visible in the PR status UI. No scan is enqueued yet.
330
+
2.**On `workflow_run completed`** — When the named workflow finishes with a matching conclusion, Layne looks up the cached PR metadata and enqueues the scan. If the cache is cold (e.g. Layne was offline when the PR was opened), Layne falls back to the GitHub API to find the associated PR.
331
+
332
+
### Failure mode
333
+
334
+
If the watched workflow is renamed or removed, Layne never receives the `workflow_run` event and the scan never runs. To fail **closed** (safe) rather than **open** (silent), make Layne's Check Run a **required status check** in branch protection — then a missing check blocks merging and the absence is immediately visible.
335
+
336
+
### Examples
337
+
338
+
**Scan only after CI passes (mirrors GitHub's "require approval for workflows" gate):**
339
+
```json
340
+
{
341
+
"owner/repo": {
342
+
"trigger": {
343
+
"on": "workflow_run",
344
+
"workflow": "Tests Done"
345
+
}
346
+
}
347
+
}
348
+
```
349
+
350
+
**Scan regardless of whether CI passes or fails (workflow was approved, code is worth scanning):**
351
+
```json
352
+
{
353
+
"owner/repo": {
354
+
"trigger": {
355
+
"on": "workflow_run",
356
+
"workflow": "Tests Done",
357
+
"conclusions": ["success", "failure"]
358
+
}
359
+
}
360
+
}
361
+
```
362
+
363
+
**Apply workflow_run trigger to all repos globally:**
364
+
```json
365
+
{
366
+
"$global": {
367
+
"trigger": {
368
+
"on": "workflow_run",
369
+
"workflow": "CI"
370
+
}
371
+
}
372
+
}
373
+
```
374
+
375
+
---
376
+
292
377
## Notifications
293
378
294
379
Layne can send a notification to a chat webhook when a scan finds new issues. Notifications fire after the GitHub Check Run is fully posted — engineers see the check result first, then receive the alert.
Copy file name to clipboardExpand all lines: docs/local-development.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -130,17 +130,17 @@ Everything else in `.env.example` is commented out with sensible defaults — yo
130
130
131
131
### 3.3 Add your test repo to the scanner config
132
132
133
-
Open `config/repos.json`. Add an entry for your test repository:
133
+
Open `config/layne.json`. Add an entry for your test repository:
134
134
135
135
```json
136
136
{
137
137
"your-org/your-repo": {}
138
138
}
139
139
```
140
140
141
-
An empty object uses the global defaults: Semgrep and Trufflehog enabled, Claude disabled, plus any global notifications and labels defined in `config/repos.json`. In the checked-in example config, Rocket.Chat and Slack notifications are enabled globally, but local development still works fine if you leave the webhook env vars unset because the notifiers will log and skip delivery.
141
+
An empty object uses the global defaults: Semgrep and Trufflehog enabled, Claude disabled, plus any global notifications and labels defined in `config/layne.json`. In the checked-in example config, Rocket.Chat notifications are enabled globally, but local development still works fine if you leave the webhook env vars unset because the notifier will log and skip delivery.
142
142
143
-
> **Important:**The worker reads`config/repos.json` once at startup. Restart it after any changes.
143
+
> **Important:**Both the server and worker read`config/layne.json` once at startup. Restart both after any changes.
144
144
145
145
---
146
146
@@ -446,4 +446,4 @@ npm run dev:stop
446
446
|`[replay] Error: could not reach the server`| Server is not running |`npm start` in another terminal |
447
447
|`[replay] response → 401 Invalid signature`|`GITHUB_WEBHOOK_SECRET` in `.env` does not match what the server expects | They must be identical — both processes load the same `.env`|
448
448
| Check Run never appears on GitHub | The worker failed to authenticate | Check worker logs for `getInstallationToken` errors; verify `GITHUB_APP_PRIVATE_KEY` is a valid single-line PEM |
449
-
| Config changes not picked up by the worker | Worker caches `config/repos.json` at startup | Restart the worker: `Ctrl-C` then `npm run worker`|
449
+
| Config changes not picked up | Server and worker each cache `config/layne.json` at startup | Restart both: `Ctrl-C`on each, then`npm start` and`npm run worker`|
Copy file name to clipboardExpand all lines: docs/reference.md
+1-2Lines changed: 1 addition & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,8 +15,7 @@
15
15
|`DEBUG_MODE`| No | off | Set to `true` or `1` to enable verbose debug logging |
16
16
|`METRICS_ENABLED`| No |`false`| Set to `true` to enable Prometheus metrics endpoints |
17
17
|`METRICS_PORT`| No |`9091`| Port for the worker Prometheus metrics server |
18
-
|`ROCKETCHAT_WEBHOOK_URL`| No | — | Global Rocket.Chat webhook URL, referenced as `"$ROCKETCHAT_WEBHOOK_URL"` in `config/repos.json`. Add additional vars (e.g. `PAYMENTS_ROCKETCHAT_WEBHOOK_URL`) for per-repo webhooks. |
19
-
|`SLACK_WEBHOOK_URL`| No | — | Global Slack incoming webhook URL, referenced as `"$SLACK_WEBHOOK_URL"` in `config/repos.json`. Add additional vars (e.g. `PAYMENTS_SLACK_WEBHOOK_URL`) for per-repo webhooks. |
18
+
|`ROCKETCHAT_WEBHOOK_URL`| No | — | Global Rocket.Chat webhook URL, referenced as `"$ROCKETCHAT_WEBHOOK_URL"` in `config/layne.json`. Add additional vars (e.g. `PAYMENTS_ROCKETCHAT_WEBHOOK_URL`) for per-repo webhooks. |
0 commit comments