Dashboard › publish › Session 0TStcDyR5xKS
0TStcDyR5xKShgsR5Review the current uncommitted release-approval authorization implementation in /home/byk/Code/getsentry/publish. Do not edit files. Assess correctness, security, GitHub Actions behavior, and missing tests. Report only substantive findings with severity and file:line references; end with MERGE or DO-NOT-MERGE.
No files found
M .github/workflows/publish.yml
?? src/modules/__tests__/approval-authorizer.js
?? src/modules/approval-authorizer.js
?? src/publish/authorize-approval.js
.github/workflows/publish.yml | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cff6bc3..44b6b33 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -32,6 +32,29 @@ jobs:
&& github.event.issue.state == 'open'
&& startsWith(github.event.issue.title, 'publish: ')
steps:
+ - name: Get publish code
+ uses: actions/checkout@v6
+ with:
+ persist-credentials: false
+
+ # The release bot sees all target repositories. This checks the actor's
+ # current effective target-repository role without exposing RBAC data. ...
<path>/home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js</path>
<type>file</type>
<content>
1: const ALLOWED_ROLE_NAMES = new Set(["write", "maintain", "admin"]);
2: const AUTO_APPROVER = "getsantry[bot]";
3: const PUBLISH_TITLE_REGEX = /^publish: (?<owner>[^/]+)\/(?<repository>[^/@]+)(?:\/[^@]+)?@[^@]+$/;
4:
5: async function authorizeApproval({ actor, issueTitle, getPermission }) {
6: const title = issueTitle.match(PUBLISH_TITLE_REGEX);
7:
8: if (!title || title.groups.owner !== "getsentry") {
9: return { authorized: false, repository: null };
10: }
11:
12: const repository = `${title.groups.owner}/${title.groups.repository}`;
13:
14: if (actor === AUTO_APPROVER) {
15: return { authorized: true, repository };
16: }
17:
18: const { role_name } = await getPermission({
19: owner: title.groups.owner,
20: repository: title.groups.repository,
21: username: actor,
22: });
23:
24: return { authorized: ...
<path>/home/byk/Code/getsentry/publish/src/publish/authorize-approval.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const { authorizeApproval } = require("../modules/approval-authorizer.js");
3:
4: async function getPermission({ owner, repository, username }) {
5: const response = await fetch(
6: `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/collaborators/${encodeURIComponent(username)}/permission`,
7: {
8: headers: {
9: Accept: "application/vnd.github+json",
10: Authorization: `Bearer ${process.env.APPROVAL_TOKEN}`,
11: "X-GitHub-Api-Version": "2026-03-10",
12: },
13: }
14: );
15:
16: if (!response.ok) {
17: throw new Error(
18: `Could not retrieve ${username}'s permission for ${owner}/${repository}: GitHub returned ${response.status}`
19: );
20: }
21:
22: return response.json();
23: }
24:
25: async function main() {
26: if ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/approval-authorizer.js</path>
<type>file</type>
<content>
1: import { describe, expect, test, vi } from "vitest";
2:
3: const {
4: authorizeApproval,
5: } = require("../approval-authorizer.js");
6:
7: const issueTitle = "publish: getsentry/sentry-javascript@10.0.0";
8:
9: describe("authorizeApproval", () => {
10: test("allows the trusted auto-approval bot without a target repository lookup", async () => {
11: const getPermission = vi.fn();
12:
13: await expect(
14: authorizeApproval({
15: actor: "getsantry[bot]",
16: issueTitle,
17: getPermission,
18: })
19: ).resolves.toEqual({ authorized: true, repository: "getsentry/sentry-javascript" });
20:
21: expect(getPermission).not.toHaveBeenCalled();
22: });
23:
24: test.each(["write", "maintain", "admin"])(
25: "allows a target repository %s collaborator",
26: async (roleName) => {
27: const ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/publish.yml</path>
<type>file</type>
<content>
1: name: Publish
2: on:
3: issues:
4: types: [labeled]
5:
6: concurrency:
7: # Use the issue title (e.g. "publish: getsentry/foo@1.2.3") so duplicate
8: # issues for the same repo@version share a concurrency group.
9: group: ${{ github.event.issue.title }}
10: cancel-in-progress: false
11:
12: permissions:
13: contents: read
14: issues: write
15: packages: write
16:
17: jobs:
18: # When accepted is added to a publish issue:
19: # - Add ci-pending (and remove ci-failed if retrying)
20: # - Enable the poller via CI_POLLER_HAS_PENDING=true
21: # - Comment on the issue
22: # - Trigger the poller immediately so we don't wait for the next cron tick
23: # The publish job below requires ci-ready, so it will not fire until the
24: # poller flips ci-pending β ci-ready (which also prevents publishing without
25: # CI verification in the ...
Found 51 matches
/home/byk/Code/getsentry/publish/README.md:
Line 18: F -->|"Add 'accepted' label"| G[Publish workflow triggers]
Line 38: 1. Add the [**`accepted`**](https://github.com/getsentry/publish/labels/accepted) label to initiate publishing. Since this action requires elevated permissions, you may need to ask your team lead or manager
Line 91: Automatic approvals are managed in the [`auto-approve.yml`](https://github.com/getsentry/publish/blob/main/.github/workflows/auto-approve.yml) workflow.
/home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js:
Line 2: const AUTO_APPROVER = "getsantry[bot]";
/home/byk/Code/getsentry/publish/src/modules/update-issue.js:
Line 72: name: "accepted",
/home/byk/Code/getsentry/publish/src/modules/__tests__/approval-authorizer.js:
Line 8: test("allows the trusted auto-approval bot without a target repository lookup", async () => {
Line 13: actor: ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/auto-approve.yml</path>
<type>file</type>
<content>
1: name: auto-approve non-sdks
2: on:
3: issues:
4: types: [opened]
5:
6: permissions:
7: contents: read
8:
9: jobs:
10: auto-approve:
11: runs-on: ubuntu-latest
12: if: |
13: (github.actor == 'sentry-release-bot[bot]' || github.actor == 'getsantry[bot]') &&
14: startsWith(github.event.issue.title, 'publish: ')
15: steps:
16: - uses: actions/checkout@v4
17: with:
18: sparse-checkout: auto-approve-repos.txt
19: sparse-checkout-cone-mode: false
20: - name: Get auth token
21: id: token
22: uses: actions/create-github-app-token@v3
23: with:
24: client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
25: private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
26: - name: Auto-approve if repo is in the list
27: env:
28: GH_TOKEN: ${{ ...
<path>/home/byk/Code/getsentry/publish/src/publish/inputs.js</path>
<type>file</type>
<content>
1: const github = require("@actions/github");
2: const core = require("@actions/core");
3: const { detailsFromContext } = require("../modules/details-from-context");
4:
5: async function inputs() {
6: const result = await detailsFromContext({
7: context: github.context,
8: });
9: core.setOutput("result", result);
10: }
11:
12: inputs();
(End of file - total 12 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: /**
2: * Matches the entire "Targets" section of a github publish issue body.
3: */
4: const TARGETS_SECTION_PARSER_REGEX =
5: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
6:
7: /**
8: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
9: * The "id" of the targets is captured within a capture group.
10: */
11: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
12:
13: /**
14: * Matches checked targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
15: * The "id" of the targets is captured within a capture group.
16: */
17: const CHECKED_TARGETS_PARSER_REGEX = /^\s*- \[x\] (\S+)/gim;
18:
19: async function detailsFromContext({ context }) {
20: if (!context || !context.payload || ...
<path>/home/byk/Code/getsentry/publish/README.md</path>
<type>file</type>
<content>
1: # Sentry Publish π
2:
3: This is a meta/control repository that implements the [Central Publish Repository](docs/rfc.md) RFC
4:
5: ## Quick Start
6:
7: [craft quick start](https://craft.sentry.dev/github-actions/)
8:
9: ## Release Flow
10:
11: ```mermaid
12: flowchart TD
13: A[Developer triggers release workflow] --> B["SDK Repo: craft prepare"]
14: B --> C[Build artifacts & create release branch]
15: C --> D[Upload artifacts to GitHub]
16: D --> E["Create issue in getsentry/publish"]
17: E --> F{Release Manager Review}
18: F -->|"Add 'accepted' label"| G[Publish workflow triggers]
19: G --> I[Download artifacts from GitHub]
20: I --> J["craft publish to registries"]
21: J --> K{Publish successful?}
22: K -->|Yes| L[Issue closed - success]
23: K -->|No| M[Issue updated with failure]
24: ```
25:
26: ## Goals
27:
28: 1. ...
<path>/home/byk/Code/getsentry/publish/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "publish",
3: "private": true,
4: "version": "0.0.1",
5: "description": "Approval-based publishing system for Sentry",
6: "main": "index.js",
7: "repository": "git@github.com:getsentry/publish.git",
8: "author": "Sentry Open Source <oss@sentry.io>",
9: "license": "Apache-2.0",
10: "volta": {
11: "node": "24.0.0",
12: "yarn": "1.22.22"
13: },
14: "resolutions": {
15: "undici": "^6.23.0"
16: },
17: "scripts": {
18: "test": "vitest run",
19: "test:watch": "vitest",
20: "lint": "eslint src .github --ignore-pattern '!.github'",
21: "prettier": "prettier --write src"
22: },
23: "devDependencies": {
24: "eslint": "^8.9.0",
25: "eslint-config-prettier": "^8.3.0",
26: "eslint-plugin-yml": "^0.13.0",
27: "prettier": "^2.2.1",
28: "vitest": "^4.1.0"
29: },
30: "dependencies": {
31: "@actions/core": ...
yarn run v1.22.22 $ vitest run src/modules/__tests__/approval-authorizer.js [1m[46m RUN [49m[22m [36mv4.0.17 [39m[90m/home/byk/Code/getsentry/publish[39m [32mβ[39m src/modules/__tests__/approval-authorizer.js [2m([22m[2m12 tests[22m[2m)[22m[32m 12[2mms[22m[39m [2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m [2m Tests [22m [1m[32m12 passed[39m[22m[90m (12)[39m [2m Start at [22m 15:14:51 [2m Duration [22m 217ms[2m (transform 24ms, setup 0ms, import 41ms, tests 12ms, environment 0ms)[22m Done in 0.67s. yarn run v1.22.22 $ eslint src .github --ignore-pattern '!.github' /home/byk/Code/getsentry/publish/.github/workflows/cocoapods-keepalive.yml 1:7 error Must use plain style scalar yml/plain-scalar β 1 problem (1 error, 0 warnings) 1 error and 0 warnings potentially fixable with the `--fix` option. error Command failed with exit code 1. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this ...
<path>/home/byk/Code/getsentry/publish/auto-approve-repos.txt</path> <type>file</type> <content> 1: getsentry/arroyo 2: getsentry/auto-type-annotate 3: getsentry/devenv 4: getsentry/infra-event-notifier 5: getsentry/jest-sentry-environment 6: getsentry/json-schema-diff 7: getsentry/js-source-scopes 8: getsentry/objectstore/clients 9: getsentry/ophio 10: getsentry/pdb 11: getsentry/pyo3-python-tracing-subscriber 12: getsentry/pytest-sentry 13: getsentry/relay/py 14: getsentry/responses 15: getsentry/rust-proguard 16: getsentry/rust-sourcemap 17: getsentry/rust-usage-accountant 18: getsentry/script-runner 19: getsentry/sentry-api-schema 20: getsentry/sentry-forked-djangorestframework-stubs 21: getsentry/sentry-forked-django-stubs 22: getsentry/sentry-forked-jsonnet 23: getsentry/sentry-infra-tools 24: getsentry/sentry-kafka-management 25: getsentry/sentry-kafka-schemas 26: getsentry/sentry-protos 27: getsentry/sentry-redis-tools 28: getsentry/service-registry 29: getsentry/skrooge 30: ...
rg: regex parse error:
(?:publish: (?!getsentry/))
^^^
error: look-around, including look-ahead and look-behind, is not supported
Consider enabling PCRE2 with the --pcre2 flag, which can handle backreferences
and look-around.
Found 10 matches /home/byk/Code/getsentry/publish/src/publish/__tests__/authorize-approval.js: Line 34: issueTitle: "publish: getsentry/sentry-javascript@10.0.0", Line 60: "publish: getsentry/sentry-javascript@10.0.0"; /home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js: Line 4: /^publish: (?<owner>[^/]+)\/(?<repository>[^/@]+)(?:\/[^@]+)?@[^@]+$/; /home/byk/Code/getsentry/publish/src/modules/details-from-context.js: Line 25: /^publish: (?:getsentry\/)?(?<repo>[^/@]+)(?<path>\/[\w./-]+)?@(?<version>[\w.+-]+)$/; /home/byk/Code/getsentry/publish/src/modules/__tests__/approval-authorizer.js: Line 5: const issueTitle = "publish: getsentry/sentry-javascript@10.0.0"; Line 71: issueTitle: "publish: example/other@1.0.0", Line 85: issueTitle: "publish: getsentry/sentry-javascript", /home/byk/Code/getsentry/publish/src/modules/__tests__/details-from-context.js: Line 11: title: "publish: ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/authorize-approval.js</path>
<type>file</type>
<content>
1: import { afterEach, describe, expect, test, vi } from "vitest";
2: import fs from "fs";
3: const { main } = require("../authorize-approval.js");
4:
5: const originalEnvironment = { ...process.env };
6:
7: afterEach(() => {
8: process.env = { ...originalEnvironment };
9: vi.restoreAllMocks();
10: vi.resetModules();
11: });
12:
13: async function runAuthorization({ actor, issueTitle, response }) {
14: process.env.GITHUB_OUTPUT = "/tmp/github-output";
15: process.env.APPROVAL_TOKEN = "release-bot-token";
16: process.env.APPROVAL_ACTOR = actor;
17: process.env.APPROVAL_ISSUE_TITLE = issueTitle;
18:
19: const appendFileSync = vi
20: .spyOn(fs, "appendFileSync")
21: .mockImplementation(() => {});
22: global.fetch = vi.fn().mockResolvedValue(response);
23:
24: await main();
25: await vi.waitFor(() => ...
.github/workflows/publish.yml src/libs/__tests__/github.js src/libs/github.js src/modules/post-workflow-details.js src/modules/process-end-state.js src/modules/update-issue.js src/publish/inputs.js src/publish/post-result.js src/publish/post-workflow-details.js src/publish/update-issue.js error: pathspec 'src/publish/__tests__/authorize-approval.js' did not match any file(s) known to git Did you forget to 'git add'?
M .github/workflows/publish.yml M src/libs/__tests__/github.js M src/libs/github.js M src/modules/post-workflow-details.js M src/modules/process-end-state.js M src/modules/update-issue.js M src/publish/inputs.js M src/publish/post-result.js M src/publish/post-workflow-details.js M src/publish/update-issue.js ?? src/modules/__tests__/approval-authorizer.js ?? src/modules/approval-authorizer.js ?? src/publish/__tests__/authorize-approval.js ?? src/publish/authorize-approval.js
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cff6bc3..081a17b 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -32,6 +32,11 @@ jobs:
&& github.event.issue.state == 'open'
&& startsWith(github.event.issue.title, 'publish: ')
steps:
+ - name: Get publish code
+ uses: actions/checkout@v6
+ with:
+ persist-credentials: false
+
- name: Get auth token
id: token
uses: actions/create-github-app-token@v3
@@ -39,6 +44,41 @@ jobs:
client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
+ # The release bot sees all target repositories. This checks the actor's
+ # current effective target-repository role without exposing RBAC data. ...
<path>/home/byk/Code/getsentry/publish/src/publish/authorize-approval.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const { authorizeApproval } = require("../modules/approval-authorizer.js");
3:
4: async function getPermission({ owner, repository, username }) {
5: const response = await fetch(
6: `https://api.github.com/repos/${encodeURIComponent(
7: owner
8: )}/${encodeURIComponent(repository)}/collaborators/${encodeURIComponent(
9: username
10: )}/permission`,
11: {
12: headers: {
13: Accept: "application/vnd.github+json",
14: Authorization: `Bearer ${process.env.APPROVAL_TOKEN}`,
15: "X-GitHub-Api-Version": "2026-03-10",
16: },
17: }
18: );
19:
20: if (!response.ok) {
21: throw new Error(
22: `Could not retrieve ${username}'s permission for ${owner}/${repository}: GitHub returned ${response.status}`
23: );
24: }
25:
26: return response.json();
27: }
28:
29: async ...
Found 16 matches
/home/byk/Code/getsentry/publish/README.md:
Line 95: The system uses [Craft](https://github.com/getsentry/craft) under the hood to prepare and publish releases. It uses tokens from [Sentry Release Bot](https://github.com/apps/sentry-release-bot), which is a GitHub App that is installed on all repos in `getsentry` with read and write access to code, PRs, and actions. We utilize the [create-github-app-token](https://github.com/actions/create-github-app-token) to generate a short live token in every action run, with `SENTRY_RELEASE_BOT_CLIENT_ID` and `SENTRY_RELEASE_BOT_PRIVATE_KEY` defined at the organization level.
/home/byk/Code/getsentry/publish/src/modules/approval-authorizer.js:
Line 2: const AUTO_APPROVER = "getsantry[bot]";
/home/byk/Code/getsentry/publish/src/modules/__tests__/approval-authorizer.js:
Line 13: actor: "getsantry[bot]",
/home/byk/Code/getsentry/publish/.github/workflows/publish.yml:
Line 44: client-id: ${{ ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/ci-poller.yml</path>
<type>file</type>
<content>
60: # Uses a subshell so GH_TOKEN override doesn't leak to other calls.
61: # Captures stdout and exits non-zero on API failure (including 404s)
62: # so callers can reliably detect errors.
63: gh_api_release() {
64: local output
65: output=$(GH_TOKEN="$RELEASE_TOKEN" gh api "$@" 2>&1)
66: local exit_code=$?
67: if [[ $exit_code -ne 0 ]]; then
68: echo "::warning::gh api failed: $output" >&2
69: return $exit_code
70: fi
71: printf '%s' "$output"
72: }
73: # Only check issues that have BOTH ci-pending AND accepted labels.
74: # This avoids polling for abandoned releases that nobody approved. ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/publish.yml</path>
<type>file</type>
<content>
30: if: >-
31: github.event.label.name == 'accepted'
32: && github.event.issue.state == 'open'
33: && startsWith(github.event.issue.title, 'publish: ')
34: steps:
35: - name: Get publish code
36: uses: actions/checkout@v6
37: with:
38: persist-credentials: false
39:
40: - name: Get auth token
41: id: token
42: uses: actions/create-github-app-token@v3
43: with:
44: client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
45: private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
46:
47: # The release bot sees all target repositories. This checks the actor's
48: # current effective target-repository role without exposing RBAC data. ...
StatusCode: non 2xx status code (404 GET https://docs.github.com/en/rest/repos/collaborators#get-repository-permissions-for-a-user)
StatusCode: non 2xx status code (404 GET https://docs.github.com/en/rest/repos/collaborators)
Found 3 matches
/home/byk/Code/getsentry/publish/src/publish/__tests__/authorize-approval.js:
Line 42: "https://api.github.com/repos/getsentry/sentry-javascript/collaborators/contractor/permission",
/home/byk/Code/getsentry/publish/src/publish/authorize-approval.js:
Line 6: `https://api.github.com/repos/${encodeURIComponent(
Line 15: "X-GitHub-Api-Version": "2026-03-10",
HIGH β src/modules/approval-authorizer.js:5-24: No separation-of-duties check exists. A release requester with write, maintain, or admin access can add accepted to their own release issue and pass authorization. The workflow never supplies requester identity, so this cannot enforce the required second-person approval. No regression test covers self-approval.
HIGH β src/modules/approval-authorizer.js:14-16: getsantry[bot] bypasses all target-repository authorization. The acceptance gate does not verify that the label came from the allowlisted auto-approval path, so any accepted label event by this bot authorizes any getsentry release. This also violates the second-person requirement.
MEDIUM β src/modules/approval-authorizer.js:3,8: Authorization rejects titles without an explicit getsentry/ owner, while the existing publish parser accepts them (src/modules/details-from-context.js:24-25). Valid requests such as publish: sentry@21.3.1 will have accepted removed. No test covers this supported title form.
DO-NOT-MERGE
Date: Aug 27, 2026 * π‘ (15:33) Assistant code-review finding (HIGH; DO-NOT-MERGE): `src/modules/approval-authorizer.js:5-24` has no separation-of-duties check; a release requester with `write`, `maintain`, or `admin` access can add `accepted` to their own release issue and pass authorization. The workflow does not supply requester identity, preventing enforcement of the required second-person apβ¦
Date: Aug 27, 2026 * π’ (15:25) Tool returned a non-2xx `404` for `GET https://docs.github.com/en/rest/repos/collaborators#get-repository-permissions-for-a-user`. * π’ (15:26) Tool returned a non-2xx `404` for `GET https://docs.github.com/en/rest/repos/collaborators`.
Date: Aug 27, 2026 * π΄ (15:25) User stated that the CI poller always adds the `ci-ready` label after CI passes. * π‘ (15:25) `.github/workflows/publish.yml` approval job runs only for an `accepted` label event on an open issue whose title starts with `publish: `, and checks out code with `actions/checkout@v6` and `persist-credentials: false`. * π‘ (15:25) `.github/workflows/publish.yml` creates β¦
Date: Aug 27, 2026 * π‘ (15:24) `.github/workflows/ci-poller.yml` polls up to `200` GitHub issues carrying both `ci-pending` and `accepted` labels; if none are found, it logs `No ci-pending + accepted issues found.` * π‘ (15:24) `.github/workflows/ci-poller.yml` parses publish-issue titles in the form `"publish: owner/repo[/path]@version"` to extract `repo` and `version`. * π‘ (15:24) `.github/woβ¦
Date: Aug 27, 2026 * π‘ (15:24) Repository search found 16 matches relevant to approval/release-bot authentication. `README.md:95` says publishing uses Craft and tokens from the Sentry Release Bot GitHub App, installed on all `getsentry` repositories with read/write access to code, PRs, and actions; short-lived tokens are generated per action run using `actions/create-github-app-token` with organβ¦
Date: Aug 27, 2026 * π‘ (15:22) Attempting to inspect `src/publish/__tests__/authorize-approval.js` through Git pathspec failed because the file is untracked: `error: pathspec 'src/publish/__tests__/authorize-approval.js' did not match any file(s) known to git`; Git suggested `git add`. * π‘ (15:22) Working tree additionally contains formatting-only modifications to `src/libs/__tests__/github.js`β¦
Date: Aug 27, 2026 * π΄ [requested-review] (15:12) User requested a read-only review of the current uncommitted release-approval authorization implementation in `/home/byk/Code/getsentry/publish`; requested assessment of correctness, security, GitHub Actions behavior, and missing tests; requested only substantive findings with severity and `file:line` references, ending in `MERGE` or `DO-NOT-MERGβ¦