Skip to content

Commit f183d61

Browse files
committed
update to messages
1 parent f21be13 commit f183d61

7 files changed

Lines changed: 74 additions & 55 deletions

File tree

.claude/skills/add-test-fixture/SKILL.md

Lines changed: 25 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,77 +6,54 @@ description: Creates a new test event fixture directory under tests/events/{even
66

77
## Critical
88

9-
- Every fixture directory requires **three files**`GithubMessageBuilderTest.php` reads all three; missing any will cause a fatal test error:
9+
- Every fixture directory requires **exactly three files**`GithubMessageBuilderTest.php` skips any directory missing `expected_text.txt`. The required files are:
1010
```
1111
tests/events/deployment/type.txt
1212
tests/events/deployment/payload.json
1313
tests/events/deployment/expected_text.txt
1414
```
15-
- `expected_text.txt` contains plain-text chat message output — **generate it by running the test harness**, not by hand.
16-
- The value in `tests/events/deployment/type.txt` must match the `X-GitHub-Event` header value exactly (e.g. `push`, `pull_request`, `ping`) — no trailing newline issues; the test trims it.
15+
- `expected_text.txt` contains **plain text** (no IRC color codes). Write the expected `$Msg['text']` output from `GithubMessageBuilder::build()` directly.
16+
- Do NOT create `expected.bin` or `discord.json` — those are for the removed `IrcConverter`/`DiscordConverter` and will be silently ignored.
17+
- The value in `type.txt` must match the `X-GitHub-Event` header value exactly (e.g. `push`, `pull_request`) — `GithubMessageBuilderTest.php` trims it.
1718

1819
## Instructions
1920

2021
1. **Create the fixture directory.**
21-
Use the GitHub event name as the directory name (e.g. `deployment`, `workflow_dispatch`). For action variants, append `_{action}` (e.g. `pull_request_merged`, `issue_closed`). Verify no existing directory with that name exists under `tests/events/`.
22+
```bash
23+
mkdir tests/events/deployment
24+
```
25+
Use the GitHub event name as the directory name. For action variants, append `_{action}` (e.g. `pull_request_merged`, `issues_closed`).
2226

2327
2. **Write the event type file.**
24-
Content is the bare event type only — no action suffix, no newline required (the test trims it). Matches the value GitHub sends in the `X-GitHub-Event` HTTP header.
25-
Write `deployment` to `tests/events/deployment/type.txt`.
28+
```bash
29+
echo -n 'deployment' > tests/events/deployment/type.txt
30+
```
31+
Bare event type only — no action suffix, no trailing newline required.
2632

2733
3. **Write the payload file.**
28-
Save a real GitHub webhook payload to `tests/events/deployment/payload.json`. Mirror the formatting of existing fixtures — tabs for indentation, `JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES` style:
29-
```json
30-
{
31-
"action": "created",
32-
"sender": {
33-
"login": "octocat",
34-
"id": 1,
35-
"avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/1?v=3"
36-
},
37-
"repository": {
38-
"id": 1296269,
39-
"name": "Hello-World",
40-
"full_name": "octocat\/Hello-World",
41-
"html_url": "https:\/\/github.com\/octocat\/Hello-World",
42-
"private": false
43-
}
44-
}
45-
```
46-
Verify `tests/events/deployment/payload.json` is valid JSON:
34+
Copy a real payload from `log/` (structure is `{"repo":"...","event":"...","data":{...}}`; use the `data` value) or from GitHub docs. Save as `tests/events/deployment/payload.json`. Validate:
4735
```bash
4836
php -r "json_decode(file_get_contents('tests/events/deployment/payload.json')); echo json_last_error();"
4937
```
5038
Must print `0`.
5139

52-
4. **Generate `expected_text.txt` using the test suite.**
53-
Run the tests — the new fixture will fail and print the actual output. Use that output as the content of `expected_text.txt`, then re-run to confirm it passes:
54-
```bash
55-
vendor/bin/phpunit tests/GithubMessageBuilderTest.php
40+
4. **Determine the expected text.**
41+
Instantiate `GithubMessageBuilder` with the event type and payload, call `build()['text']`, and save the result:
42+
```php
43+
$builder = new GithubMessageBuilder('deployment', json_decode(file_get_contents('tests/events/deployment/payload.json'), true));
44+
echo $builder->build()['text'];
5645
```
46+
Or generate it via a quick CLI script and write to `tests/events/deployment/expected_text.txt`.
5747

5848
5. **Verify the fixture passes.**
5949
```bash
60-
vendor/bin/phpunit tests/GithubMessageBuilderTest.php
50+
vendor/bin/phpunit
6151
```
62-
All tests must pass.
63-
64-
## Examples
65-
66-
**User says:** "Add a test fixture for the `deployment` event"
67-
68-
**Actions taken:**
69-
1. Create `tests/events/deployment/`
70-
2. Write `tests/events/deployment/type.txt``deployment`
71-
3. Write `tests/events/deployment/payload.json` with a real GitHub deployment payload
72-
4. Run `vendor/bin/phpunit tests/GithubMessageBuilderTest.php`, capture actual output, write to `tests/events/deployment/expected_text.txt`
73-
5. Re-run tests to verify they pass
74-
75-
**Result:** `tests/events/deployment/` contains all three files; `vendor/bin/phpunit tests/GithubMessageBuilderTest.php` exits green.
52+
All tests must pass. The fixture is picked up automatically via `DirectoryIterator` on `tests/events/`.
7653

7754
## Common Issues
7855

79-
- **`file_get_contents(...): Failed to open stream`**one of the three required files is missing. Check `tests/events/deployment/` contains `type.txt`, `payload.json`, `expected_text.txt`.
80-
- **`assertEquals failed`**`expected_text.txt` content doesn't match actual output. Re-run the test, capture actual output, update the file.
81-
- **New fixture not picked up by test runner**confirm the directory is directly under `tests/events/` (not nested). The test uses a single-level `DirectoryIterator`.
82-
- **`GetEventType()` assertion fails**`tests/events/deployment/type.txt` contains extra whitespace or wrong casing. The value must exactly match the GitHub `X-GitHub-Event` header (all lowercase, underscores, e.g. `pull_request` not `Pull-Request`).
56+
- **Fixture not tested**`expected_text.txt` is missing. The test provider skips directories without this file.
57+
- **`assertSame` fails with whitespace difference**`expected_text.txt` has a trailing newline. The test `trim()`s the file content, so trailing newlines are fine, but mid-string differences (e.g. extra space) will fail.
58+
- **`json_decode` returns null**`payload.json` has invalid JSON. Validate with `php -r "var_dump(json_decode(file_get_contents('...')));"` — must not print `NULL`.
59+
- **New fixture not picked up**confirm the directory is directly under `tests/events/` (not nested two levels deep).

CALIBER_LEARNINGS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@ Auto-managed by [caliber](https://github.com/caliber-ai-org/ai-setup) — do not
88
- **[gotcha:project]** `composer.json` has no `autoload`, `autoload-dev`, or `scripts` sections — there is no PSR-4 autoloader and no `composer test` shortcut. Classes (`GithubWebhook`, `IgnoredEventException`, etc.) are loaded via manual `require` statements. Tests must bootstrap these manually or via a PHPUnit bootstrap file.
99
- **[pattern:project]** Real GitHub webhook payloads live in `log/` as JSON files with structure `{"repo": "...", "event": "...", "data": {...payload...}}`. The `data` key holds the raw webhook payload — use these as authoritative source material when creating new fixtures under `tests/events/`.
1010
- **[gotcha:project]** In the `push` event handler, `$Msg['alias']` is set from `$Message['head_commit']['author']['name']` (commit author name), **not** `$Message['pusher']['name']`. These differ when CI or bots push commits authored by a human. Code that compares commit authors against the pusher must use `$Msg['alias']`, not `$Message['pusher']['name']`.
11+
- **[fix:project]** `phpunit.xml` must have `bootstrap="tests/bootstrap.php"` set — without it PHPUnit cannot load source classes since there is no PSR-4 autoloader. The bootstrap file manually `require`s all `src/` classes.
12+
- **[gotcha:project]** `composer.json` now has a `scripts` section — `composer test`, `composer analyse`, `composer cs-fix`, and `composer cs-check` all work. The old stale learning about no scripts is now superseded.
13+
- **[convention:project]** Test fixture directories under `tests/events/{event_name}/` now use **3 files**: `payload.json`, `type.txt`, and `expected_text.txt` (plain text, no IRC color codes). The old 4-file format with `expected.bin` and `discord.json` was for the removed `IrcConverter`/`DiscordConverter` — do NOT create those files.
14+
- **[pattern:project]** `GithubMessageBuilder` (in `src/GithubMessageBuilder.php`) handles all message formatting. The `switch ($EventType)` block in `web/github.php` is now routing-only: call `$Builder->build()` before the switch, then each case just decides whether to call `SendToChat` or skip it. Do NOT rebuild `$Msg` inside individual cases.
15+
- **[gotcha:project]** `release` edited events have a `changes` key in the payload that is an **empty array** `[]` — GitHub does not populate what fields changed. There is no `from`/`to` data available for release edits; the message can only show the current release state, not what was modified.
16+
- **[gotcha:project]** `status` events (CI status from AppVeyor, Scrutinizer, Travis, etc.) are NOT ignored in the current codebase — they fall through to `default:` which emits a generic "triggered a status event" message. The payload has rich fields: `state` (success/failure/error/pending), `description` (CI message), `context` (CI service name), `target_url` (link to build), and `commit.commit.message`. Add a dedicated `case 'status':` handler in `GithubMessageBuilder::buildText()` to surface these fields.

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ vendor/bin/php-cs-fixer fix
1919
- **Config**: `src/config.php` (gitignored) — defines `GITHUB_WEBHOOKS_SECRET` and `$chatChannels['rocketchat']` / `$chatChannels['teams']`
2020
- **Logs**: `log/` — JSON files named `Ymd_His_eventtype_action_user_repo.json`
2121
- **Tests**: `tests/` via `phpunit.xml` · fixtures in `tests/events/{event_name}/`
22-
- **Quality**: `phpstan.neon` · `.php-cs-fixer.dist.php` (PSR2 + PHP74Migration)
22+
- **Quality**: `phpstan.neon` · `phpstan-bootstrap.php` · `.php-cs-fixer.dist.php` (PSR2 + PHP74Migration)
2323
- **CI**: `.github/` — GitHub Actions workflows (`.github/workflows/ci.yml`)
2424

2525
## Event Handling Pattern
@@ -36,7 +36,7 @@ file_put_contents(__DIR__.'/../log/'.date('Ymd_His').'_'.$EventType
3636
.(isset($Message['action']) ? '_'.$Message['action'] : '')
3737
.'_'.$User.'_'.str_replace(['/', '-', ' '], '_', $RepositoryName).'.json',
3838
json_encode($log, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
39-
// 3. Route by event type
39+
// 3. Route by event type — $Builder->build() called before switch
4040
switch ($EventType) {
4141
case 'push': /* ... */ SendToChat('notifications', $Msg, $useRC, $useTeams); break;
4242
default: SendToChat('notifications', $Msg, $useRC, $useTeams); break;

phpstan-bootstrap.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
// Stub constants defined in gitignored src/config.php so PHPStan can analyse without it.
5+
if (!defined('GITHUB_WEBHOOKS_SECRET')) {
6+
define('GITHUB_WEBHOOKS_SECRET', '');
7+
}

phpstan.neon

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
parameters:
22
checkMissingIterableValueType: false
33
level: 6
4+
bootstrapFiles:
5+
- phpstan-bootstrap.php
46
paths:
57
- .
68
excludes_analyse:
79
- vendor
810
- examples
911
- public
10-
ignoreErrors:
11-
- message: "/Access to an undefined property object::.*/"
12-
path: src/*.php

web/github-old.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
require __DIR__ . '/../src/IgnoredEventException.php';
1818
require __DIR__ . '/../src/NotImplementedException.php';
1919

20-
$Hook = new GitHubWebHook();
20+
$Hook = new GithubWebhook();
2121
try {
2222
if (!$Hook->ValidateHubSignature(GITHUB_WEBHOOKS_SECRET)) {
2323
throw new Exception('Secret validation failed.');

web/github.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
require __DIR__ . '/../src/IgnoredEventException.php';
1818
require __DIR__ . '/../src/NotImplementedException.php';
1919

20-
$Hook = new GitHubWebHook();
20+
$Hook = new GithubWebhook();
2121
try {
2222
if (!$Hook->ValidateHubSignature(GITHUB_WEBHOOKS_SECRET)) {
2323
throw new Exception('Secret validation failed.');
@@ -267,6 +267,35 @@
267267
SendToChat('notifications', $Msg, $useRC, $useTeams);
268268
break;
269269

270+
case 'status':
271+
$CiState = $Message['state'] ?? 'unknown';
272+
$CiContext = $Message['context'] ?? 'CI';
273+
$CiDescription = $Message['description'] ?? '';
274+
$CiUrl = $Message['target_url'] ?? '';
275+
$CommitUrl = $Message['commit']['html_url'] ?? '';
276+
$CommitSha = substr($Message['sha'] ?? '', 0, 7);
277+
$CommitMsg = $Message['commit']['commit']['message'] ?? '';
278+
$CommitMsg = strtok($CommitMsg, "\n");
279+
$StateEmoji = match ($CiState) {
280+
'success' => '',
281+
'failure' => '',
282+
'error' => '🚨',
283+
'pending' => '',
284+
default => 'ℹ️',
285+
};
286+
$CiLink = $CiUrl ? "[{$CiContext}]({$CiUrl})" : "**{$CiContext}**";
287+
$CommitLink = $CommitUrl ? "[`{$CommitSha}`]({$CommitUrl})" : "`{$CommitSha}`";
288+
$ChatMsg = "{$StateEmoji} **{$CiState}** — {$CiLink} on [{$RepositoryName}](https://github.com/{$RepositoryName}) {$CommitLink}";
289+
if ($CiDescription !== '') {
290+
$ChatMsg .= "\n> {$CiDescription}";
291+
}
292+
if ($CommitMsg !== '') {
293+
$ChatMsg .= "\n> _{$CommitMsg}_";
294+
}
295+
$Msg['text'] = $ChatMsg;
296+
SendToChat('notifications', $Msg, $useRC, $useTeams);
297+
break;
298+
270299
default:
271300
$ChatMsg = "ℹ️ {$Msg['alias']} triggered a **{$EventType}** event "
272301
. (isset($Message['action']) ? "({$Message['action']}) " : "")
@@ -314,6 +343,7 @@ function SendToChat(string $Where, array $Payload, bool $useRC = true, bool $use
314343
{
315344
error_log("Sending Payload ".json_encode($Payload)." to {$Where}");
316345
global $chatChannels;
346+
$Code = 0;
317347
if ($useRC === true) {
318348
$Url = $chatChannels['rocketchat'][$Where];
319349
$c = curl_init();

0 commit comments

Comments
 (0)