Docs: environment reviews in webhooks, notifications, audit log and limits; runner claim (#122)
#116 added required reviewers and wait timers, and #118 closed the personal-token runner claim. The webhook pages lacked deployment_review, the notifications page its notification and email, the audit log deployment.approve and deployment.reject, and Limits the reviewer, wait-timer and comment limits. The API reference still said a personal token could claim runner jobs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016q5wwBFck8JHq14gHUM2CL
Canberk Demirci authored · GitHub committed signed1 parent 4d87efbcommit 1d1a71f7c1aa48232fdfe8fe713ca7d26c6312c2Browse files
6 files changed+64−8
Files (6)
modifiedapps/diffinery/docs/accounts/notifications.md+2−0View file
| @@ -32,6 +32,7 @@ | ||
| 32 | 32 | - issue and pull request activity you take part in, when **Participating → On Diffinery** is on, |
| 33 | 33 | - activity in repositories you watch with **All activity**, when **Watching → On Diffinery** is on, |
| 34 | 34 | - failed workflow runs you started, when **Actions → On Diffinery** is on, |
| 35 | +- a run waiting for your approval to deploy to an environment where you are a [required reviewer](/docs/actions/environments#required-reviewers-and-wait-timers) ("Deployment to production in acme/website is waiting for your review"), when **Actions → On Diffinery** is on, | |
| 35 | 36 | - someone inviting you by username to an organization or a repository (kind `invitation`, for example "<admin> invited you to acme as member"); it opens **Settings → Invitations** (see [Invitations you receive](/docs/accounts/invitations#invitations-you-receive)), |
| 36 | 37 | - someone giving you a repository role at once, without an invitation, because you are a member of the organization that owns the repository or already a collaborator on it (kind `repo access`, for example "<admin> gave you write access to acme/website"). This comes from **Add people**, the role menu on **Settings → Collaborators** or the API. |
| 37 | 38 | |
| @@ -227,6 +228,7 @@ | ||
| 227 | 228 | | --- | --- | --- | --- | --- | |
| 228 | 229 | | `thread` | `[acme/website] Cache build artifacts (PR #42)` | Participating or Watching | `author`, `comment`, `mention`, `assign`, `review_requested`, `manual`, `subscribed` or `your_activity` | Activity on an issue or pull request (see the next table) | |
| 229 | 230 | | `run` | `[acme/website] Run failed: CI - main (3f9c2a1)` | Actions | `ci_activity` | A workflow run you started failed or timed out (or succeeded, if you asked for all runs) | |
| 231 | +| `deployment_review_requested` | `[acme/website] Deployment to production is waiting for your review` | Actions | `review_requested` | A run waits for a required reviewer of an environment, and you are one of them (not when you started the run) | | |
| 230 | 232 | | `signin_new` | `[Diffinery] New sign-in to your account` | Security | `security_alert` | A passkey sign-in from a browser and passkey combination not seen before, or any email-recovery sign-in | |
| 231 | 233 | | `passkey_added` | `[Diffinery] A passkey was added to your account` | Security | `security_alert` | You added a passkey in settings or with a sign-in link | |
| 232 | 234 | | `passkey_removed` | `[Diffinery] A passkey was removed from your account` | Security | `security_alert` | You removed a passkey | |
modifiedapps/diffinery/docs/reference/limits.md+4−0View file
| @@ -67,6 +67,10 @@ | ||
| 67 | 67 | | Check run name | 255 characters | `POST .../check-runs`. | |
| 68 | 68 | | Deployment `required_contexts` | 100 contexts | `POST .../deployments`. | |
| 69 | 69 | | Environment name | 1 to 255 characters | No `/`, `\` or control characters. | |
| 70 | +| Required reviewers | 6 people per environment | `PUT .../environments/{env}`: `422 an environment can have at most 6 required reviewers`. See [Required reviewers and wait timers](/docs/actions/environments#required-reviewers-and-wait-timers). | | |
| 71 | +| Wait timer | 0 to 43,200 minutes (30 days) | `wait_timer` on `PUT .../environments/{env}`. | | |
| 72 | +| Waiting for a reviewer | 30 days | A job still waiting for its environment's reviewers after 30 days fails. | | |
| 73 | +| Deployment review comment | 4,000 characters | `POST .../actions/runs/{run_id}/pending_deployments`: `422 comment is longer than 4000 characters`. | | |
| 70 | 74 | |
| 71 | 75 | ## Search |
| 72 | 76 |
modifiedapps/diffinery/docs/reference/webhook-events.md+51−2View file
| @@ -5,7 +5,7 @@ | ||
| 5 | 5 | |
| 6 | 6 | This page lists every event a Diffinery [webhook](/docs/repositories/webhooks) can deliver, the actions of each event, and the exact payload fields, with an example for each. Use it to write or adapt a webhook receiver. |
| 7 | 7 | |
| 8 | -There are two kinds of payload. `push`, `pull_request`, `pull_request_review`, `issues`, `issue_comment` and `workflow_run` have small Diffinery payloads with ids and links. `release`, `status`, `deployment`, `deployment_status`, `repository_dispatch` and `workflow_dispatch` have GitHub's payload structure. | |
| 8 | +There are two kinds of payload. `push`, `pull_request`, `pull_request_review`, `issues`, `issue_comment` and `workflow_run` have small Diffinery payloads with ids and links. `release`, `status`, `deployment`, `deployment_status`, `deployment_review`, `repository_dispatch` and `workflow_dispatch` have GitHub's payload structure. | |
| 9 | 9 | |
| 10 | 10 | ## Common structure |
| 11 | 11 | |
| @@ -25,7 +25,7 @@ | ||
| 25 | 25 | | Field | Type | Description | |
| 26 | 26 | |---|---|---| |
| 27 | 27 | | `action` | string | The action, for events that have one. Absent for `push`, `status` and `workflow_dispatch`. | |
| 28 | -| `at` | string | ISO 8601 time the event happened. In the small payloads and in `workflow_dispatch`; `release`, `status`, `deployment`, `deployment_status` and `repository_dispatch` have no `at` (use their own `created_at` and `updated_at` fields). | | |
| 28 | +| `at` | string | ISO 8601 time the event happened. In the small payloads and in `workflow_dispatch`; `release`, `status`, `deployment`, `deployment_status`, `deployment_review` and `repository_dispatch` have no `at` (use their own `created_at` and `updated_at` fields). | | |
| 29 | 29 | | `repository` | object | The repository (see below) | |
| 30 | 30 | | `sender` | object | The account that caused the event (see below) | |
| 31 | 31 | |
| @@ -669,6 +669,55 @@ | ||
| 669 | 669 | } |
| 670 | 670 | ``` |
| 671 | 671 | |
| 672 | +## deployment_review | |
| 673 | + | |
| 674 | +Sent when a workflow run starts waiting on an environment's [required reviewers](/docs/actions/environments#required-reviewers-and-wait-timers), and when a reviewer decides, on the run page or with `POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments`. An environment with only a wait timer sends no event. | |
| 675 | + | |
| 676 | +| Action | Sent when | Sender | | |
| 677 | +|---|---|---| | |
| 678 | +| `requested` | A job that names the environment becomes ready and has to wait for a reviewer (once per environment and run) | Who started the run | | |
| 679 | +| `approved` | A reviewer approves the environment for the run | The reviewer | | |
| 680 | +| `rejected` | A reviewer rejects it | The reviewer | | |
| 681 | + | |
| 682 | +| Field | Type | Description | | |
| 683 | +|---|---|---| | |
| 684 | +| `action` | string | `requested`, `approved` or `rejected` | | |
| 685 | +| `since` | string | When the event happened (ISO 8601) | | |
| 686 | +| `environment` | string | The environment's name | | |
| 687 | +| `environment_id` | number or null | The environment's id, as `GET .../environments` shows it | | |
| 688 | +| `state` | string | `approved` or `rejected`. Absent on `requested`. | | |
| 689 | +| `comment` | string | The reviewer's comment; empty on `requested` or when none was given | | |
| 690 | +| `workflow_run` | object | `id`, `run_number`, `name`, `display_title`, `head_sha`, `head_branch` and `html_url` of the run | | |
| 691 | +| `reviewers` | array | The environment's required reviewers, each `{ "type": "User", "reviewer": { "login", "id" } }` | | |
| 692 | +| `requestor` | object | `{ "login" }` of who started the run. Only on `requested`. | | |
| 693 | +| `approver` | object | `{ "login" }` of the reviewer who decided. Only on `approved` and `rejected`. | | |
| 694 | + | |
| 695 | +```json | |
| 696 | +{ | |
| 697 | + "action": "approved", | |
| 698 | + "since": "2026-10-13T10:12:30.000Z", | |
| 699 | + "environment": "production", | |
| 700 | + "environment_id": 4, | |
| 701 | + "comment": "Release notes look good", | |
| 702 | + "state": "approved", | |
| 703 | + "workflow_run": { | |
| 704 | + "id": "8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a", | |
| 705 | + "run_number": 128, | |
| 706 | + "name": "Deploy", | |
| 707 | + "display_title": "Release 2.4", | |
| 708 | + "head_sha": "4f1c2e9d0b7a6c5e3f2d1c0b9a8e7d6c5b4a3f2e", | |
| 709 | + "head_branch": "main", | |
| 710 | + "html_url": "https://diffinery.com/acme/website/actions/runs/8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a" | |
| 711 | + }, | |
| 712 | + "reviewers": [{ "type": "User", "reviewer": { "login": "alice", "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90" } }], | |
| 713 | + "approver": { "login": "alice" }, | |
| 714 | + "repository": { "id": 6628874657374920, "name": "website", "full_name": "acme/website", "private": false, "visibility": "public", "html_url": "https://diffinery.com/acme/website" }, | |
| 715 | + "sender": { "id": "a1b2c3d4e5f60718293a4b5c6d7e8f90", "login": "alice", "type": "User" } | |
| 716 | +} | |
| 717 | +``` | |
| 718 | + | |
| 719 | +Unlike GitHub, `workflow_run` holds only the fields above and `reviewers[].reviewer.id` is the Diffinery account id. | |
| 720 | + | |
| 672 | 721 | ## repository_dispatch |
| 673 | 722 | |
| 674 | 723 | Sent when someone with the write role calls `POST /repos/{owner}/{repo}/dispatches`. The same request starts workflows with `on: repository_dispatch` (see [Events that trigger workflows](/docs/actions/events)). |
modifiedapps/diffinery/docs/repositories/webhooks.md+4−3View file
| @@ -28,6 +28,7 @@ | ||
| 28 | 28 | | `status` | A commit status is posted | none | |
| 29 | 29 | | `deployment` | A deployment is created, with the REST API or by a workflow job with `environment:` | `created` | |
| 30 | 30 | | `deployment_status` | A deployment gets a new status, from the REST API or from a workflow job with `environment:` | `created` | |
| 31 | +| `deployment_review` | A run starts waiting on an environment's [required reviewers](/docs/actions/environments#required-reviewers-and-wait-timers), or a reviewer approves or rejects it | `requested`, `approved`, `rejected` | | |
| 31 | 32 | | `repository_dispatch` | Someone calls `POST /repos/{owner}/{repo}/dispatches` | the `event_type` that was sent | |
| 32 | 33 | | `workflow_dispatch` | Someone starts a workflow by hand (web UI, CLI, REST API or MCP) | none | |
| 33 | 34 | | `*` | Every event above | | |
| @@ -41,13 +42,13 @@ | ||
| 41 | 42 | 1. Open the repository, for example `https://diffinery.com/acme/website`. |
| 42 | 43 | 2. Select **Settings**, then **Webhooks** in the sidebar (`https://diffinery.com/acme/website/settings/webhooks`). |
| 43 | 44 | 3. Under **Add a webhook**, enter the **Payload URL**. It must start with `https://` and name a public host: not `localhost`, a private or loopback address, or Diffinery itself. |
| 44 | -4. Under **Events**, keep **Everything** checked to receive all events, or uncheck it and check the events you want: `push`, `pull_request`, `pull_request_review`, `issues`, `issue_comment`, `workflow_run`, `workflow_dispatch`. If **Everything** stays checked, the webhook receives every event regardless of the other boxes, including `release`, `status`, `deployment`, `deployment_status` and `repository_dispatch`. If you uncheck every box, the webhook also receives every event. | |
| 45 | +4. Under **Events**, keep **Everything** checked to receive all events, or uncheck it and check the events you want: `push`, `pull_request`, `pull_request_review`, `issues`, `issue_comment`, `workflow_run`, `workflow_dispatch`. If **Everything** stays checked, the webhook receives every event regardless of the other boxes, including `release`, `status`, `deployment`, `deployment_status`, `deployment_review` and `repository_dispatch`. If you uncheck every box, the webhook also receives every event. | |
| 45 | 46 | 5. Optionally enter a **Secret**. Leave it empty to have Diffinery generate one. |
| 46 | 47 | 6. Select **Add webhook**. |
| 47 | 48 | |
| 48 | 49 | The page then shows **Webhook created for** your URL with the signing secret. Copy it now: it is not shown again. If you lose it, delete the webhook and create a new one. |
| 49 | 50 | |
| 50 | -> **Note:** The web form only offers the events listed above as separate checkboxes. To subscribe to `release`, `status`, `deployment`, `deployment_status` or `repository_dispatch` without receiving everything, create the webhook with the API and list the events you want. | |
| 51 | +> **Note:** The web form only offers the events listed above as separate checkboxes. To subscribe to `release`, `status`, `deployment`, `deployment_status`, `deployment_review` or `repository_dispatch` without receiving everything, create the webhook with the API and list the events you want. | |
| 51 | 52 | |
| 52 | 53 | ### With the API |
| 53 | 54 | |
| @@ -302,7 +303,7 @@ | ||
| 302 | 303 | - The same signature scheme (HMAC-SHA256, `sha256=` prefix, hex). |
| 303 | 304 | - The same event names and the same `action` values for the actions Diffinery sends. |
| 304 | 305 | - Payloads have GitHub's full `repository` object (numeric `id`, `node_id`, `owner`, `private`, `visibility`, `default_branch`, URLs and so on), a `sender`, GitHub-style `pull_request` and `issue` objects, and `pull_request.merged` on `closed`. |
| 305 | -- `release`, `status`, `deployment`, `deployment_status` and `repository_dispatch` payloads have GitHub's structure, with the release, status and deployment fields GitHub receivers read. `workflow_dispatch` has GitHub's `inputs`, `ref` and `workflow` fields. | |
| 306 | +- `release`, `status`, `deployment`, `deployment_status`, `deployment_review` and `repository_dispatch` payloads have GitHub's structure, with the release, status and deployment fields GitHub receivers read. `workflow_dispatch` has GitHub's `inputs`, `ref` and `workflow` fields. | |
| 306 | 307 | - The hooks API returns GitHub's hook object (`type`, `name`, `config`, `last_response`, ...). |
| 307 | 308 | |
| 308 | 309 | Differences to plan for: |
modifiedapps/diffinery/docs/security/audit-log.md+2−1View file
| @@ -140,12 +140,13 @@ | ||
| 140 | 140 | |
| 141 | 141 | | Action | Recorded when | Detail | |
| 142 | 142 | |---|---|---| |
| 143 | -| `environment.update` | An environment is created or updated with `PUT /api/v3/repos/{owner}/{repo}/environments/{name}` | `environment`, `deployment_branch_policy` (as sent, or `null`) | | |
| 143 | +| `environment.update` | An environment is created or updated with `PUT /api/v3/repos/{owner}/{repo}/environments/{name}` | `environment`, `deployment_branch_policy` (as sent, or `null`); `reviewers` (logins), `wait_timer` and `prevent_self_review` when they were sent | | |
| 144 | 144 | | `environment.delete` | An environment is deleted | `environment` | |
| 145 | 145 | | `environment.branch_policy.create` | A deployment branch or tag pattern is added | `environment`, `pattern`, `type` (`branch` or `tag`) | |
| 146 | 146 | | `environment.branch_policy.delete` | A deployment branch or tag pattern is removed | `environment`, `id` | |
| 147 | 147 | | `environment.secret.set`, `environment.secret.delete` | An environment secret is saved or deleted | `environment`, `name` (never the value) | |
| 148 | 148 | | `environment.variable.set`, `environment.variable.delete` | An environment variable is created, updated, renamed or deleted | `environment`, `name`, and `renamed_from` when a `PATCH` renamed it | |
| 149 | +| `deployment.approve`, `deployment.reject` | A required reviewer approves or rejects a run's waiting deployments, on the run page or with `POST .../actions/runs/{run_id}/pending_deployments`. The actor is the reviewer. | `run`, `environments` (the environments decided), `comment` | | |
| 149 | 150 | |
| 150 | 151 | ### Git and pull requests |
| 151 | 152 |
modifiedapps/diffinery/src/docs/api-meta.ts+1−2View file
| @@ -1128,9 +1128,8 @@ | ||
| 1128 | 1128 | }, |
| 1129 | 1129 | "POST /repos/{owner}/{repo}/actions/runners/claim": { |
| 1130 | 1130 | summary: "Claim a job for a self-hosted runner", |
| 1131 | - description: "Used by `diffinery runner`. Send the runner's `drn_` token and an empty body: the runner offers the name and labels it registered with. A repository admin's personal token with the `admin` scope also works, with `name` and `labels` in the body (how runners worked before registration). Returns 204 when no queued job matches the labels, otherwise the job with a fresh job token.", | |
| 1131 | + description: "Used by `diffinery runner`. Send the runner's `drn_` token and an empty body: the runner offers the name and labels it registered with. Only registered runners claim: a personal token, even a repository admin's, gets 403 `personal tokens can't claim jobs; register this runner: ...`. Returns 204 when no queued job matches the labels, otherwise the job with a fresh job token. See /docs/actions/runners.", | |
| 1132 | 1132 | access: "admin", |
| 1133 | - body: { name: ["string", "Runner name (with a personal token only)."], labels: ["string[]", "Runner labels, matched against `runs-on` (with a personal token only)."] }, | |
| 1134 | 1133 | }, |
| 1135 | 1134 | "GET /repos/{owner}/{repo}/actions/runs": { |
| 1136 | 1135 | summary: "List workflow runs", |