@e2e-dev/github 0.4.1-nightly-20261015074742 → 0.4.2-nightly-20261016073603

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/.build.json CHANGED
@@ -1 +1 @@
1
- {"version":"0.4.1-nightly-20261015074742"}
1
+ {"version":"0.4.2-nightly-20261016073603"}
package/dist/post.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * One comment per pull request, kept current: the reporter looks for the
3
- * comment whose first line is its marker and edits it, and creates one only
4
- * when none exists. The REST calls are the three GitHub documents them as: list the
5
- * issue's comments, update a comment, create a comment.
3
+ * comment the token's own account wrote whose first line is its marker and
4
+ * edits it, and creates one only when none exists. The REST calls are the ones GitHub documents: read the token's
5
+ * account, list the issue's comments, update a comment, create a comment.
6
6
  *
7
7
  * Every request carries the reporter's abort signal. Anything GitHub answers
8
8
  * outside the protocol is an error naming itself; the runner prints one line.
package/dist/post.js CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * One comment per pull request, kept current: the reporter looks for the
3
- * comment whose first line is its marker and edits it, and creates one only
4
- * when none exists. The REST calls are the three GitHub documents them as: list the
5
- * issue's comments, update a comment, create a comment.
3
+ * comment the token's own account wrote whose first line is its marker and
4
+ * edits it, and creates one only when none exists. The REST calls are the ones GitHub documents: read the token's
5
+ * account, list the issue's comments, update a comment, create a comment.
6
6
  *
7
7
  * Every request carries the reporter's abort signal. Anything GitHub answers
8
8
  * outside the protocol is an error naming itself; the runner prints one line.
@@ -16,9 +16,11 @@ const PER_PAGE = 100;
16
16
  */
17
17
  const MAX_PAGES = 100;
18
18
  const USER_AGENT = '@e2e-dev/github';
19
- /** One GitHub REST call; anything but success is an error the runner can print. */
20
- async function request(params, method, route, body) {
21
- const response = await params.fetch(`${params.apiUrl}${route}`, {
19
+ /** What GitHub answers `GET /user` with for an installation token: `GITHUB_TOKEN` or a GitHub App's. */
20
+ const INTEGRATION_REFUSAL = 'Resource not accessible by integration';
21
+ /** Sends one GitHub REST call and hands back the response as it came. */
22
+ function send(params, method, route, body) {
23
+ return params.fetch(`${params.apiUrl}${route}`, {
22
24
  method,
23
25
  headers: {
24
26
  authorization: `Bearer ${params.token}`,
@@ -30,15 +32,50 @@ async function request(params, method, route, body) {
30
32
  ...(body === undefined ? {} : { body: JSON.stringify(body) }),
31
33
  signal: params.signal,
32
34
  });
33
- if (response.ok)
34
- return response.json().catch(() => undefined);
35
+ }
36
+ /** The error for a response the protocol has no meaning for, with the start of its body. */
37
+ async function unexpected(response, method, route) {
35
38
  const detail = (await response.text().catch(() => '')).slice(0, 200);
39
+ return new Error(`GitHub responded ${response.status} to ${method} ${route}${detail === '' ? '' : `: ${detail}`}`);
40
+ }
41
+ /** The error the runner prints for a response that is not a success. */
42
+ async function failure(params, response, method, route) {
36
43
  if (response.status === 401)
37
- throw new Error('GitHub rejected the token (GITHUB_TOKEN or GH_TOKEN)');
44
+ return new Error('GitHub rejected the token (GITHUB_TOKEN or GH_TOKEN)');
38
45
  if (response.status === 403 || response.status === 404) {
39
- throw new Error(`the token cannot comment on ${params.repository}#${params.pullRequest}: a pull request from a fork runs with a read-only token, and the job needs \`permissions: pull-requests: write\``);
46
+ return new Error(`the token cannot comment on ${params.repository}#${params.pullRequest}: a pull request from a fork runs with a read-only token, and the job needs \`permissions: pull-requests: write\``);
40
47
  }
41
- throw new Error(`GitHub responded ${response.status} to ${method} ${route}${detail === '' ? '' : `: ${detail}`}`);
48
+ return unexpected(response, method, route);
49
+ }
50
+ /** One GitHub REST call; anything but success is an error the runner can print. */
51
+ async function request(params, method, route, body) {
52
+ const response = await send(params, method, route, body);
53
+ if (response.ok)
54
+ return response.json().catch(() => undefined);
55
+ throw await failure(params, response, method, route);
56
+ }
57
+ /**
58
+ * The account the token posts as. A personal token names its user at
59
+ * `GET /user`. An installation token cannot read it, and posts as its app's
60
+ * bot, `github-actions[bot]` or `<app>[bot]`, so any bot author matches.
61
+ */
62
+ async function tokenAuthor(params) {
63
+ const response = await send(params, 'GET', '/user');
64
+ if (response.ok) {
65
+ const user = await response.json().catch(() => undefined);
66
+ if (!isRecord(user) || typeof user['login'] !== 'string')
67
+ throw new Error('GitHub answered GET /user without a login');
68
+ const login = user['login'].toLowerCase();
69
+ return (author) => typeof author['login'] === 'string' && author['login'].toLowerCase() === login;
70
+ }
71
+ if (response.status === 403) {
72
+ const refusal = await response.clone().json().catch(() => undefined);
73
+ if (isRecord(refusal) && refusal['message'] === INTEGRATION_REFUSAL)
74
+ return (author) => author['type'] === 'Bot';
75
+ }
76
+ if (response.status === 401)
77
+ throw await failure(params, response, 'GET', '/user');
78
+ throw await unexpected(response, 'GET', '/user');
42
79
  }
43
80
  /**
44
81
  * Whether a comment body is the reporter's own for this marker: its first
@@ -51,13 +88,17 @@ function carriesMarker(body, marker) {
51
88
  const firstLine = end === -1 ? body : body.slice(0, end);
52
89
  return firstLine.replace(/\r$/, '') === marker;
53
90
  }
54
- /** The id of the comment carrying the marker, or undefined when no page has one. */
55
- async function findComment(params, issue) {
91
+ /** The id of the comment the author wrote carrying the marker, or undefined when no page has one. */
92
+ async function findComment(params, issue, author) {
56
93
  for (let page = 1; page <= MAX_PAGES; page += 1) {
57
94
  const comments = await request(params, 'GET', `${issue}/comments?per_page=${PER_PAGE}&page=${page}`);
58
95
  if (!Array.isArray(comments))
59
96
  throw new Error('GitHub answered the comment list with a body that is not a list');
60
- const found = comments.find((comment) => isRecord(comment) && typeof comment['body'] === 'string' && carriesMarker(comment['body'], params.marker));
97
+ const found = comments.find((comment) => isRecord(comment) &&
98
+ isRecord(comment['user']) &&
99
+ author(comment['user']) &&
100
+ typeof comment['body'] === 'string' &&
101
+ carriesMarker(comment['body'], params.marker));
61
102
  if (isRecord(found) && typeof found['id'] === 'number')
62
103
  return found['id'];
63
104
  if (comments.length < PER_PAGE)
@@ -68,7 +109,7 @@ async function findComment(params, issue) {
68
109
  /** Creates or updates the reporter's comment and resolves with its URL. */
69
110
  export async function upsertComment(params) {
70
111
  const issue = `/repos/${params.repository}/issues/${params.pullRequest}`;
71
- const existing = await findComment(params, issue);
112
+ const existing = await findComment(params, issue, await tokenAuthor(params));
72
113
  const created = existing === undefined
73
114
  ? await request(params, 'POST', `${issue}/comments`, { body: params.body })
74
115
  : await request(params, 'PATCH', `/repos/${params.repository}/issues/comments/${existing}`, { body: params.body });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e2e-dev/github",
3
- "version": "0.4.1-nightly-20261015074742",
3
+ "version": "0.4.2-nightly-20261016073603",
4
4
  "description": "GitHub reporter for e2e: one pull request comment per run, from GitHub Actions",
5
5
  "keywords": [
6
6
  "e2e",
@@ -43,10 +43,10 @@
43
43
  "NOTICE"
44
44
  ],
45
45
  "peerDependencies": {
46
- "e2e": "0.21.1-nightly-20261015074742"
46
+ "e2e": "0.21.2-nightly-20261016073603"
47
47
  },
48
48
  "devDependencies": {
49
- "e2e": "0.21.1-nightly-20261015074742",
49
+ "e2e": "0.21.2-nightly-20261016073603",
50
50
  "@types/node": "26.6.2",
51
51
  "typescript": "7.0.2",
52
52
  "vitest": "5.0.1"