Dashboard › publish › Session 0zagU7llErLs
0zagU7llErLsZ8oKOREAD-ONLY independent audit of /home/byk/Code/getsentry/publish. Do not edit, format, generate, stage, or mutate files. Inspect the exact current worktree against origin/main. Return a non-empty evidence report covering these five critical questions only: 1. Can any newer unlabeled, malformed, renamed, request-mutated, or re-approved state still pass approval/ci-ready validation? 2. Can check-suite or branch-head API failure, revision movement, or a failed ci-ready add incorrectly advance or strand a release? 3. Can arbitrary-ref workflow_dispatch or attacker-controlled issue/event data execute untrusted code with secrets or inject shell commands? 4. Does the final pre-Craft fence bind the exact approved request/revision, and are actions/Craft immutable? 5. Do success, failure, cancellation, and setup failure always deauthorize and reconcile independently of dependencies/comments/telemetry? Cite current file:line evidence for each answer and label it PASS, CONCERN, or MUST-FIX. If any tool blocks review, begin BLOCKED and quote its exact error. End exactly MERGE or DO-NOT-MERGE. Never return empty output.
I will fingerprint the current tree, enumerate every tracked and untracked delta from origin/main, then trace the five security and lifecycle paths through workflows, source, and tests. The required repo-setup skill is unavailable in this session, so I will apply the checked-in repository instructions and inspect the setup files directly; this does not block the read-only audit.
No files found
## main...origin/main MM .github/workflows/auto-approve.yml MM .github/workflows/ci-poller.yml M .github/workflows/cocoapods-keepalive.yml MM .github/workflows/publish.yml M .github/workflows/test.yml M AGENTS.md M README.md M docs/key_rotation_vault_changes.md M docs/rfc.md M src/libs/__tests__/github.js M src/libs/github.js M src/modules/__tests__/ci-poller-input.js M src/modules/__tests__/ci-poller-workflow.js MM src/modules/__tests__/details-from-context.js M src/modules/__tests__/process-end-state.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/__tests__/release-revision.js MM src/modules/__tests__/update-issue.js M src/modules/ci-poller-input.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js MM src/modules/process-end-state.js M src/modules/publish-location.js M src/modules/release-revision.js M src/modules/update-issue.js M src/publish/__tests__/discover-location.js M ...
7c60ddb7f43040fe8fbfea70efc833f689c04e75 7c60ddb7f43040fe8fbfea70efc833f689c04e75
M .github/workflows/auto-approve.yml M .github/workflows/ci-poller.yml M .github/workflows/cocoapods-keepalive.yml M .github/workflows/publish.yml M .github/workflows/test.yml M AGENTS.md M README.md M docs/key_rotation_vault_changes.md M docs/rfc.md M src/libs/__tests__/github.js M src/libs/github.js M src/modules/__tests__/ci-poller-input.js M src/modules/__tests__/ci-poller-workflow.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/process-end-state.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/__tests__/release-revision.js M src/modules/__tests__/update-issue.js M src/modules/ci-poller-input.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/modules/process-end-state.js M src/modules/publish-location.js M src/modules/release-revision.js M src/modules/update-issue.js M src/publish/__tests__/discover-location.js M src/publish/__tests__/resolve-release-revision.js M s...
.github/workflows/auto-approve.yml | 30 ++- .github/workflows/ci-poller.yml | 267 +++++++++++++++++---- .github/workflows/cocoapods-keepalive.yml | 1 - .github/workflows/publish.yml | 272 +++++++++++++++++++--- .github/workflows/test.yml | 6 +- AGENTS.md | 6 + README.md | 4 +- docs/key_rotation_vault_changes.md | 40 ++-- docs/rfc.md | 9 +- src/libs/__tests__/github.js | 2 +- src/libs/github.js | 6 +- src/modules/__tests__/ci-poller-input.js | 22 +- src/modules/__tests__/ci-poller-workflow.js | 252 +++++++++++++++++--- src/modules/__tests__/details-from-context.js | 22 +- src/modules/__tests__/process-end-state.js | 27 +++ ...
M .github/workflows/auto-approve.yml M .github/workflows/ci-poller.yml M .github/workflows/cocoapods-keepalive.yml M .github/workflows/publish.yml M .github/workflows/test.yml M AGENTS.md M README.md M docs/key_rotation_vault_changes.md M docs/rfc.md M src/libs/__tests__/github.js M src/libs/github.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/update-issue.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/modules/process-end-state.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
M .github/workflows/auto-approve.yml M .github/workflows/ci-poller.yml M .github/workflows/publish.yml M src/modules/__tests__/ci-poller-input.js M src/modules/__tests__/ci-poller-workflow.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/process-end-state.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/__tests__/release-revision.js M src/modules/__tests__/update-issue.js M src/modules/ci-poller-input.js M src/modules/process-end-state.js M src/modules/publish-location.js M src/modules/release-revision.js M src/modules/update-issue.js M src/publish/__tests__/discover-location.js M src/publish/__tests__/resolve-release-revision.js M src/publish/discover-location.js M src/publish/post-result.js M src/publish/resolve-ci-poller-input.js
.github/workflows/ci-poller-dispatch.yml .lore.md src/modules/__tests__/approval-attestation.js src/modules/__tests__/approval-authorizer.js src/modules/approval-attestation.js src/modules/approval-authorizer.js src/publish/__tests__/authorize-approval.js src/publish/__tests__/auto-approval-workflow.js src/publish/__tests__/ci-poller-workflow.js src/publish/__tests__/current-accepted-event.js src/publish/__tests__/post-result.js src/publish/__tests__/publish-workflow.js src/publish/__tests__/record-auto-approval-attestation.js src/publish/__tests__/record-ci-ready-attestation.js src/publish/__tests__/request-digest-from-event.js src/publish/__tests__/validate-approval-attestation.js src/publish/__tests__/workflow-action-pinning.js src/publish/authorize-approval.js src/publish/current-accepted-event.js src/publish/record-auto-approval-attestation.js src/publish/record-ci-ready-attestation.js src/publish/request-digest-from-event.js src/publish/validate-approval-attestation.js
No files found
<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: "generate": "node scripts/generate-publish-issue-title-parser.js",
19: "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
20: "test": "yarn check:generated && vitest run",
21: "test:watch": "vitest",
22: "lint": "eslint src .github --ignore-pattern '!.github'",
23: "prettier": "prettier --write src"
24: },
25: "devDependencies": {
26: "eslint": "^8.9.0",
27: ...
No files found
/home/byk/Code/getsentry/publish/.github/workflows/test.yml /home/byk/Code/getsentry/publish/.github/workflows/publish.yml /home/byk/Code/getsentry/publish/.github/workflows/cocoapods-keepalive.yml /home/byk/Code/getsentry/publish/.github/workflows/ci-poller.yml /home/byk/Code/getsentry/publish/.github/workflows/auto-approve.yml /home/byk/Code/getsentry/publish/.github/workflows/ci-poller-dispatch.yml
<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: issues: write
9:
10: jobs:
11: auto-approve:
12: runs-on: ubuntu-latest
13: environment: production
14: if: |
15: (github.actor == 'sentry-release-bot[bot]' || github.actor == 'getsantry[bot]') &&
16: startsWith(github.event.issue.title, 'publish: ')
17: steps:
18: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
19: with:
20: persist-credentials: false
21: - name: Get auth token
22: id: token
23: uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
24: with:
25: client-id: ${{ vars.SENTRY_INTERNAL_APP_ID }}
26: private-key: ${{ secrets.SENTRY_INTERNAL_APP_PRIVATE_KEY }}
27: - name: Bind request snapshot
28: ...
<path>/home/byk/Code/getsentry/publish/.github/workflows/ci-poller-dispatch.yml</path>
<type>file</type>
<content>
1: name: Run CI Status Poller
2:
3: on:
4: workflow_dispatch:
5:
6: permissions: {}
7:
8: jobs:
9: dispatch:
10: # production only permits deployments from main. Repository secrets must
11: # never be used by this workflow; protected environment secrets enforce
12: # that an arbitrary dispatched ref cannot add them.
13: environment: production
14: runs-on: ubuntu-latest
15: steps:
16: - run: ":"
(End of file - total 16 lines)
</content>
<path>/home/byk/Code/getsentry/publish/.github/workflows/ci-poller.yml</path> <type>file</type> <content> 1: name: CI Status Poller 2: 3: on: 4: schedule: 5: - cron: "*/5 * * * *" 6: repository_dispatch: 7: types: [ci-poller] 8: workflow_run: 9: workflows: [Run CI Status Poller] 10: types: [completed] 11: 12: permissions: 13: contents: read 14: issues: write 15: 16: jobs: 17: check-ci: 18: runs-on: ubuntu-latest 19: environment: production 20: # Skip entirely (no runner provisioned) when there's nothing to check. 21: # Set to "true" by ci-pending.yml, reset to "false" here when done. 22: # repository_dispatch and a successful main-branch run of the protected 23: # workflow_dispatch relay are the trusted manual-recovery paths. 24: if: >- 25: vars.CI_POLLER_HAS_PENDING == 'true' 26: || github.event_name == 'repository_dispatch' 27: || ( 28: github.event_name == 'workflow_run' 29: && ...
<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 ...
The current candidate is a large mixed staged/unstaged change on main, with 34 tracked files changed and 24 untracked files; HEAD and origin/main are both 7c60ddb7f43040fe8fbfea70efc833f689c04e75. The privileged workflows now use pinned action SHAs and a digest-pinned Craft image, but I am tracing whether the live-state fences and failure ordering actually enforce the claimed invariants.
<path>/home/byk/Code/getsentry/publish/src/modules/approval-attestation.js</path>
<type>file</type>
<content>
1: const crypto = require("crypto");
2:
3: const APPROVAL_ATTESTATION_PREFIX = "<!-- publish-approval ";
4: const AUTO_APPROVAL_ATTESTATION_PREFIX = "<!-- publish-auto-approval ";
5: const CI_READY_ATTESTATION_PREFIX = "<!-- publish-ci-ready ";
6: const ATTESTATION_SUFFIX = " -->";
7:
8: function createAttestation(prefix, value) {
9: return `${prefix}${Buffer.from(JSON.stringify(value)).toString(
10: "base64url"
11: )}${ATTESTATION_SUFFIX}`;
12: }
13:
14: function requestDigest({ body, labels, title }) {
15: if (
16: typeof body !== "string" ||
17: !Array.isArray(labels) ||
18: typeof title !== "string"
19: ) {
20: throw new Error("Invalid publish request");
21: }
22:
23: return crypto
24: .createHash("sha256")
25: .update(
26: JSON.stringify({
27: body,
28: dryRun: labels.some((label) => label.name === ...
<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 { parsePublishTitle } = require("./details-from-context.js");
3:
4: const AUTO_APPROVAL_LABELER = "sentry-internal-app[bot]";
5: const AUTO_APPROVERS = new Set(["getsantry[bot]", "sentry-release-bot[bot]"]);
6:
7: function isAutoApprovedRepository({
8: actor,
9: autoApprovedRepositories,
10: issueTitle,
11: }) {
12: if (!AUTO_APPROVERS.has(actor)) {
13: return false;
14: }
15:
16: const title = parsePublishTitle(issueTitle);
17:
18: if (!title) {
19: return false;
20: }
21:
22: return autoApprovedRepositories.has(
23: `getsentry/${title.repo}${title.path || ""}`
24: );
25: }
26:
27: async function authorizeApproval({ actor, issueTitle, getPermission }) {
28: const title = parsePublishTitle(issueTitle);
29:
30: if (!title) {
31: 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 {
3: createApprovalAttestation,
4: currentAcceptedEvent,
5: hasAutoApprovalAttestation,
6: requestDigest,
7: } = require("../modules/approval-attestation.js");
8: const {
9: AUTO_APPROVAL_LABELER,
10: AUTO_APPROVERS,
11: authorizeApproval,
12: isAutoApprovedRepository,
13: } = require("../modules/approval-authorizer.js");
14:
15: function getAutoApprovedRepositories() {
16: return new Set(
17: fs
18: .readFileSync("auto-approve-repos.txt", "utf8")
19: .split(/\r?\n/)
20: .filter(Boolean)
21: );
22: }
23:
24: async function getPermission({ owner, repository, username }) {
25: const response = await getGitHubResponse(
26: `repos/${encodeURIComponent(owner)}/${encodeURIComponent(
27: repository
28: )}/collaborators/${encodeURIComponent(username)}/permission`
29: );
30:
31: if ...
<path>/home/byk/Code/getsentry/publish/src/publish/validate-approval-attestation.js</path>
<type>file</type>
<content>
1: const {
2: currentAcceptedEvent,
3: currentCiReadyEvent,
4: hasApprovalAttestation,
5: hasCiReadyAttestation,
6: requestDigest,
7: } = require("../modules/approval-attestation.js");
8:
9: async function getGitHubResponse(path) {
10: return fetch(`https://api.github.com/${path}`, {
11: headers: {
12: Accept: "application/vnd.github+json",
13: Authorization: `Bearer ${process.env.APPROVAL_TOKEN}`,
14: "X-GitHub-Api-Version": "2026-03-10",
15: },
16: });
17: }
18:
19: async function getIssue({ repository, issueNumber }) {
20: const response = await getGitHubResponse(
21: `repos/${repository}/issues/${issueNumber}`
22: );
23:
24: if (!response.ok) {
25: throw new Error(
26: `Could not retrieve issue #${issueNumber}: GitHub returned ${response.status}`
27: );
28: }
29:
30: return ...
<path>/home/byk/Code/getsentry/publish/src/publish/current-accepted-event.js</path>
<type>file</type>
<content>
1: const { currentAcceptedEvent } = require("../modules/approval-attestation.js");
2: const { getAllPages } = require("./validate-approval-attestation.js");
3:
4: async function getCurrentAcceptedEvent({
5: getIssueEvents,
6: issueNumber,
7: repository,
8: }) {
9: const event = currentAcceptedEvent(
10: await getIssueEvents({ repository, issueNumber })
11: );
12:
13: if (!event) {
14: throw new Error("The issue has no current accepted label event");
15: }
16:
17: return event;
18: }
19:
20: async function main() {
21: for (const name of [
22: "APPROVAL_TOKEN",
23: "APPROVAL_ISSUE_NUMBER",
24: "APPROVAL_ISSUE_REPOSITORY",
25: ]) {
26: if (!process.env[name]) {
27: throw new Error(`No "${name}" environment variable found`);
28: }
29: }
30:
31: const event = await getCurrentAcceptedEvent({
32: getIssueEvents: ({ ...
<path>/home/byk/Code/getsentry/publish/src/publish/record-ci-ready-attestation.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3: createCiReadyAttestation,
4: currentAcceptedEvent,
5: requestDigest,
6: } = require("../modules/approval-attestation.js");
7: const {
8: getAllPages,
9: getGitHubResponse,
10: getIssue,
11: } = require("./validate-approval-attestation.js");
12:
13: async function getAuthenticatedLogin() {
14: const response = await getGitHubResponse("user");
15:
16: if (!response.ok) {
17: throw new Error(
18: `Could not retrieve the authenticated user: GitHub returned ${response.status}`
19: );
20: }
21:
22: const { login } = await response.json();
23:
24: if (typeof login !== "string") {
25: throw new Error("GitHub returned no authenticated user login");
26: }
27:
28: return login;
29: }
30:
31: async function recordCiReadyAttestation({
32: expectedAcceptedEvent,
33: ...
<path>/home/byk/Code/getsentry/publish/src/publish/record-auto-approval-attestation.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3: createAutoApprovalAttestation,
4: requestDigest,
5: } = require("../modules/approval-attestation.js");
6: const { getIssue } = require("./validate-approval-attestation.js");
7:
8: async function recordAutoApprovalAttestation({
9: autoApprover,
10: expectedRequestDigest,
11: getIssue,
12: issueNumber,
13: issueTitle,
14: repository,
15: }) {
16: const issue = await getIssue({ repository, issueNumber });
17:
18: if (
19: issue.state !== "open" ||
20: issue.title !== issueTitle ||
21: requestDigest(issue) !== expectedRequestDigest ||
22: issue.user?.login?.toLowerCase() !== autoApprover.toLowerCase()
23: ) {
24: throw new Error("The automated approval request changed before approval");
25: }
26:
27: return createAutoApprovalAttestation({ autoApprover, issue });
28: }
29:
30: ...
<path>/home/byk/Code/getsentry/publish/src/publish/request-digest-from-event.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const { requestDigest } = require("../modules/approval-attestation.js");
3:
4: function requestDigestFromEvent(event) {
5: if (!event || typeof event.issue !== "object" || event.issue === null) {
6: throw new Error("The GitHub event has no issue snapshot");
7: }
8:
9: return requestDigest(event.issue);
10: }
11:
12: function main() {
13: if (!process.env.GITHUB_EVENT_PATH) {
14: throw new Error('No "GITHUB_EVENT_PATH" environment variable found');
15: }
16:
17: if (!process.env.GITHUB_OUTPUT) {
18: throw new Error('No "GITHUB_OUTPUT" environment variable found');
19: }
20:
21: const event = JSON.parse(
22: fs.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8")
23: );
24: fs.appendFileSync(
25: process.env.GITHUB_OUTPUT,
26: `request_digest=${requestDigestFromEvent(event)}\n`
27: );
28: ...
<path>/home/byk/Code/getsentry/publish/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const { getReleaseRevisionDetails } = require("./release-revision");
3: const { isPublishPath } = require("./publish-location");
4: const {
5: isPublishRepository,
6: isReleaseVersion,
7: } = require("./publish-issue-validation");
8:
9: /**
10: * Matches the entire "Targets" section of a github publish issue body.
11: */
12: const TARGETS_SECTION_PARSER_REGEX =
13: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
14:
15: /**
16: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
17: * The "id" of the targets is captured within a capture group. ...
<path>/home/byk/Code/getsentry/publish/src/modules/ci-poller-input.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const {
3: getReleaseRevision,
4: updateReleaseRevision,
5: } = require("./release-revision");
6: const {
7: isPublishRepository,
8: isReleaseVersion,
9: } = require("./publish-issue-validation");
10: const { isPublishPath } = require("./publish-location");
11: const { requestDigest } = require("./approval-attestation");
12:
13: function getCiPollerInput({ title, issueBody, labels, revision }) {
14: let parsedTitle;
15: try {
16: parsedTitle = parsePublishIssueTitle(title);
17: } catch {
18: throw new Error(`Invalid publish issue title: '${title}'`);
19: }
20: const { repo, version } = parsedTitle;
21: if (!isPublishRepository(repo)) {
22: throw new Error(`Invalid publish issue repository: '${repo}'`);
23: }
24: if (!isReleaseVersion(version)) {
25: throw new ...
<path>/home/byk/Code/getsentry/publish/src/publish/resolve-ci-poller-input.js</path>
<type>file</type>
<content>
1: const { getCiPollerInput } = require("../modules/ci-poller-input");
2: const { readFileSync } = require("fs");
3:
4: const issueBody = process.env.PUBLISH_ISSUE_BODY_FILE
5: ? readFileSync(process.env.PUBLISH_ISSUE_BODY_FILE, "utf8")
6: : process.env.PUBLISH_ISSUE_BODY || "";
7:
8: process.stdout.write(
9: JSON.stringify(
10: getCiPollerInput({
11: issueBody,
12: labels: JSON.parse(process.env.PUBLISH_ISSUE_LABELS || ""),
13: title: process.env.PUBLISH_TITLE || "",
14: revision: process.env.PUBLISH_REVISION || "",
15: })
16: )
17: );
(End of file - total 17 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const { parse } = require("./publish-issue-title");
2:
3: function isRevision(revision) {
4: return /^[0-9a-f]{40}$/.test(revision);
5: }
6:
7: function getReleaseRevisionDetails({ issueBody, repo }) {
8: if (parse(issueBody, { startRule: "CheckRunsLinkCount" }) !== 1) {
9: throw new Error(
10: `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`
11: );
12: }
13:
14: let details;
15: try {
16: details = parse(issueBody, { startRule: "ReleaseRevision" });
17: } catch {
18: throw new Error(
19: `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
20: );
21: }
22:
23: if (details.repo !== repo) {
24: throw new Error(
25: `Expected a View check runs link for getsentry/${repo} in the publish issue body.`
26: );
27: }
28:
29: return details;
30: }
31:
32: ...
<path>/home/byk/Code/getsentry/publish/src/publish/resolve-release-revision.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { getReleaseRevision } = require("../modules/release-revision");
3:
4: function resolveReleaseRevision() {
5: const { repo } = JSON.parse(process.env.PUBLISH_ARGS || "{}");
6: if (!repo) {
7: throw new Error("Publish input must define a repository.");
8: }
9:
10: core.setOutput(
11: "revision",
12: getReleaseRevision({
13: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
14: repo,
15: })
16: );
17: }
18:
19: resolveReleaseRevision();
(End of file - total 19 lines)
</content>
Found 14 matches
/home/byk/Code/getsentry/publish/src/publish/__tests__/discover-location.js:
Line 9: vi.restoreAllMocks();
/home/byk/Code/getsentry/publish/src/publish/__tests__/publish-workflow.js:
Line 50: const guard = waitingForCi.indexOf("name: Revoke stranded approval");
Line 101: expect(section).toContain('--remove-label "ci-ready"');
/home/byk/Code/getsentry/publish/src/publish/__tests__/authorize-approval.js:
Line 16: vi.restoreAllMocks();
/home/byk/Code/getsentry/publish/src/modules/__tests__/update-issue.js:
Line 87: test("restore publish state", async () => {
Line 172: test("restores targets when accepted was already removed", async () => {
/home/byk/Code/getsentry/publish/src/modules/__tests__/ci-poller-workflow.js:
Line 140: *"/check-suites"*)
Line 333: "issue edit 1 -R getsentry/publish --remove-label ci-ready"
Line 339: "issue edit 1 -R getsentry/publish --remove-label ci-ready"
Line 350: ["check-suite ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/ci-poller-workflow.js</path>
<type>file</type>
<content>
1: import {
2: mkdtempSync,
3: mkdirSync,
4: readFileSync,
5: readdirSync,
6: rmSync,
7: writeFileSync,
8: } from "fs";
9: import { tmpdir } from "os";
10: import { join } from "path";
11: import { spawnSync } from "child_process";
12:
13: import { afterEach, expect, test } from "vitest";
14:
15: const temporaryDirectories = [];
16: const INITIAL_REVISION = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
17: const UPDATED_REVISION = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
18:
19: function getPollerScript() {
20: const workflow = readFileSync(
21: join(__dirname, "../../../.github/workflows/ci-poller.yml"),
22: "utf8"
23: );
24: const section = workflow.match(
25: / {6}- name: Check CI status for ci-pending issues[\s\S]*? {8}run: \|\n(?<script>[\s\S]*?)\n {6}- name: Check for remaining pending issues/
26: );
27:
28: if ...
<path>/home/byk/Code/getsentry/publish/src/modules/publish-issue-validation.js</path>
<type>file</type>
<content>
1: function isPublishRepository(name) {
2: return (
3: /^[A-Za-z0-9_.-]+$/.test(name) &&
4: name !== "." &&
5: name !== ".." &&
6: name !== "__proto__" &&
7: !name.startsWith("-")
8: );
9: }
10:
11: function isReleaseVersion(version) {
12: return /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([0-9a-z-]+(?:\.[0-9a-z-]+)*))?(?:\+([0-9a-z-]+(?:\.[0-9a-z-]+)*))?$/i.test(
13: version
14: );
15: }
16:
17: module.exports = { isPublishRepository, isReleaseVersion };
(End of file - total 17 lines)
</content>
<path>/home/byk/Code/getsentry/publish/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function resolvePublishLocation({ path, workspaceNames }) {
2: if (!isPublishPath(path)) {
3: throw new Error("Invalid publish path.");
4: }
5:
6: if (!workspaceNames.every(isWorkspaceName)) {
7: throw new Error(
8: "Craft workspace discovery returned an invalid workspace list."
9: );
10: }
11:
12: if (path === ".") {
13: return { path };
14: }
15:
16: const workspace = path.slice(2);
17: if (workspaceNames.includes(workspace)) {
18: return { path: ".", workspace };
19: }
20:
21: return { path };
22: }
23:
24: function isPublishPath(path) {
25: return (
26: typeof path === "string" &&
27: (path === "." ||
28: (path.startsWith("./") &&
29: path.slice(2).split("/").every(isSafeWorkspaceSegment)))
30: );
31: }
32:
33: function isWorkspaceName(name) {
34: return (
35: typeof name === "string" && ...
<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", JSON.stringify(result));
10: }
11: inputs();
(End of file - total 11 lines)
</content>
Found 36 matches /home/byk/Code/getsentry/publish/README.md: Line 68: workflow_dispatch: Line 102: This repo is read-only for everyone except release managers. Secret-bearing jobs use the protected `production` environment, which permits only `main` deployments and does not allow administrator bypass. This protects environment-scoped credentials from arbitrary workflow refs. See getsentry/sentry#21930 for an example. Line 104: `SENTRY_INTERNAL_APP_PRIVATE_KEY` and `SENTRY_RELEASE_BOT_PRIVATE_KEY` are organization-level secrets. Their availability to other repositories is an organization-wide security concern that this repository cannot narrow; Security must manage that boundary separately. /home/byk/Code/getsentry/publish/.lore.md: Line 11: * **production environment secret boundary**: Chose the protected \`production\` environment over repository- or organization-scoped publishing secrets because its \`main\`-only policy and disabled admin bypass gate credential access. ...
<path>/home/byk/Code/getsentry/publish/src/publish/__tests__/validate-approval-attestation.js</path>
<type>file</type>
<content>
1: import { afterEach, describe, expect, test, vi } from "vitest";
2:
3: const {
4: validateApprovalAttestation,
5: } = require("../validate-approval-attestation.js");
6: const {
7: createApprovalAttestation,
8: createCiReadyAttestation,
9: requestDigest,
10: } = require("../../modules/approval-attestation.js");
11:
12: afterEach(() => {
13: vi.restoreAllMocks();
14: });
15:
16: function jsonResponse(json) {
17: return { ok: true, json: vi.fn().mockResolvedValue(json) };
18: }
19:
20: function issue(
21: title,
22: { body = "", labels = [{ name: "accepted" }], state = "open" } = {}
23: ) {
24: return { body, labels, state, title };
25: }
26:
27: describe("validateApprovalAttestation", () => {
28: test("accepts the current accepted event and its trusted attestation", async () => {
29: const title = "publish: ...
Found 27 matches
/home/byk/Code/getsentry/publish/src/modules/__tests__/publish-workflow.js:
Line 109: expect(workflow).toContain("github.event.label.name == 'ci-ready'");
Line 114: "contains(github.event.issue.labels.*.name, 'ci-ready')"
/home/byk/Code/getsentry/publish/src/modules/__tests__/generate-publish-issue-title-parser.js:
Line 30: test("rejects malformed generated title grammar markers", () => {
/home/byk/Code/getsentry/publish/src/modules/__tests__/ci-poller-workflow.js:
Line 90: *record-ci-ready-attestation.js)
Line 94: if [[ "\${RECORD_CI_READY_MODE:-success}" == "malformed" ]]; then
Line 159: if [[ "\${FAIL_CI_READY_ADD:-false}" == "true" && "$*" == *"--add-label ci-ready"* ]]; then
Line 247: expect(poller.log).not.toContain("--add-label ci-ready");
Line 274: ["malformed JSON", "not JSON"],
Line 302: expect(poller.log).not.toContain("--add-label ci-ready");
Line 312: ["CI-ready proof output is malformed", { ...
<path>/home/byk/Code/getsentry/publish/src/modules/__tests__/approval-attestation.js</path>
<type>file</type>
<content>
1: import { describe, expect, test } from "vitest";
2:
3: const {
4: createApprovalAttestation,
5: createCiReadyAttestation,
6: currentAcceptedEvent,
7: hasApprovalAttestation,
8: hasCiReadyAttestation,
9: parseApprovalAttestation,
10: requestDigest,
11: } = require("../approval-attestation.js");
12:
13: function issue(title, { body = "", dryRun = false } = {}) {
14: return {
15: body,
16: labels: dryRun ? [{ name: "dry-run" }] : [],
17: title,
18: };
19: }
20:
21: describe("approval attestations", () => {
22: test("matches the latest accepted event to a trusted attestation", () => {
23: const title = "publish: getsentry/relay/py@1.2.3";
24: const publishIssue = issue(title);
25: const attestation = createApprovalAttestation({
26: actor: "contractor",
27: eventId: "200",
28: issue: publishIssue,
29: ...
🔴 (17:15) User directive/preference: Never return empty output. 🔴 (17:15) [requested-review] User requested a READ-ONLY independent audit of `/home/byk/Code/getsentry/publish`, comparing the exact current worktree against `origin/main`. 🔴 (17:15) User prohibited editing, formatting, generating, staging, or otherwise mutating files during the audit. 🟡 (17:15) User requested a non-empty evidenc…