Dashboard › craft › Session 1Kzt7k6gN2EL
1Kzt7k6gN2ELbjzQOStrict read-only final review. Inspect current working trees and full branch diffs in /home/byk/Code/getsentry/craft-workspace-action-propagation and /home/byk/Code/getsentry/publish-workspace-acceptance. Do not edit.
Verify all earlier blockers: release revision only derives from the action-created request header (Requested by, Merge target, Quick links), complete forged Quick-links blocks in changelog/code cannot select it, exactly one check-runs link fail-closed, ci poller shared parser/update preserves manual workflow_dispatch; legacy JSON cli/v2 remains usable but Action inputs safe; notification after revision resolution; exact revision paths; state parity; parser freshness; hygiene. Note that src/modules/post-workflow-details.js and src/publish/inputs.js in Publish have known newline-only unrelated dirty changes explicitly excluded from intended staging. Confirm no pnpm-lock. Return prioritized file:line findings and MERGE/DO-NOT-MERGE verdict.
<skill_content name="jj-guide"> # Skill: jj-guide # jj Guide for AI Agents Jujutsu (jj) is a Git-compatible VCS with mutable commits, automatic snapshotting, no staging area, and first-class conflicts. This skill teaches you how to use it safely from a non-interactive agent environment. If `.jj/` exists in the repo root, this is a jj repo. **Use `jj` commands, not `git`.** In a colocated repo (`.jj/` *and* `.git/`), git tools can read the state, but mutations should go through `jj` so the operation log stays consistent. ## Critical Rules - **NEVER** use interactive flags (`-i`, `--interactive`). TUI prompts hang in agent environments. This applies to `jj split -i`, `jj squash -i`, `jj commit -i`, `jj resolve`, `jj diffedit`, etc. - **ALWAYS** pass `-m "msg"` when describing/committing. Without `-m`, an editor opens and hangs. - **VERIFY** mutations with `jj st` and `jj log` after `squash`, `abandon`, `rebase`, `restore`, `commit`. ...
<path>/home/byk/Code/getsentry</path> <type>directory</type> <entries> cli-api-1423/ cli-wt/ cli/ codecov-action/ craft-vercel-prebuilt-output/ craft-vercel-project-config/ craft-workspace-action-propagation/ craft-workspaces-schema/ craft/ devinfra-coder-infra/ devinfra-deployment-service/ getsentry/ ops/ package_grant_publish_repo/ publish-workspace-acceptance/ publish/ security-as-code/ self-hosted/ sentry-api-schema/ sentry-docs/ sentry-forge/ sentry-go/ sentry-javascript/ sentry-orbital/ sentry-python/ sentry-release-registry/ sentry-wizard/ sentry/ snuba/ spotlight/ sqlish/ static-sites/ stripe-app/ symbolic/ (34 entries) </entries>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation</path> <type>directory</type> <entries> .craft.yml .dockerignore .eslintcache .git .github/ .gitignore .lore.md .nojekyll .npmignore .prettierignore .prettierrc.yml .tsbuildinfo .vscode/ action.yml AGENTS.md blog-post-draft.md build.mjs CHANGELOG.md CONTRIBUTING.md dist/ Dockerfile docs/ eslint.config.mjs img/ LICENSE node_modules/ package.json pnpm-lock.yaml README.md src/ tsconfig.build.json tsconfig.json vitest.config.mts (33 entries) </entries>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance</path> <type>directory</type> <entries> .eslintrc.js .git .github/ .gitignore AGENTS.md auto-approve-repos.txt docs/ LICENSE node_modules/ package.json README.md scripts/ src/ vitest.config.js yarn.lock (15 entries) </entries>
M action.yml M docs/src/content/docs/targets/github.md M src/__tests__/action.test.ts M src/__tests__/config.test.ts M src/commands/__tests__/publish.test.ts M src/commands/publish.ts M src/config.ts M src/index.ts M src/schemas/project_config.ts ?? src/commands/workspace.ts ?? src/commands/workspace_cmds/ feat/workspace-action-propagation 6dae341 (HEAD -> feat/workspace-action-propagation, origin/feat/workspace-action-propagation) feat: propagate release workspaces 21270a1 (origin/master, origin/HEAD) feat(config): add release workspaces 32f5462 fix(git): prevent fatal error for missing 'origin' remote (#871) 5d0ec43 fix(registry): retry registry push with backoff on concurrent updates (#870) 35e8063 meta: Bump new development version b2524ef Merge branch 'release/2.30.1' cd1e829 (tag: 2.30.1) release: 2.30.1 06180e3 fix(vercel): pass prebuilt output directory 29ad5c3 meta: Bump new development version c02eb03 Merge branch 'release/2.30.0' c79aff0 (tag: 2.30.0) release: ...
M .github/workflows/ci-poller.yml M .github/workflows/publish.yml M docs/publish-issue-format.md M package.json M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-workflow.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/publish/inputs.js M yarn.lock ?? scripts/ ?? src/modules/__tests__/ci-poller-input.js ?? src/modules/__tests__/publish-location.js ?? src/modules/__tests__/release-revision.js ?? src/modules/ci-poller-input.js ?? src/modules/publish-issue-title.js ?? src/modules/publish-issue-title.peggy ?? src/modules/publish-location.js ?? src/modules/release-revision.js ?? src/publish/resolve-ci-poller-input.js ?? src/publish/resolve-location.js ?? src/publish/resolve-release-revision.js feat/workspace-acceptance b658ffa (HEAD -> feat/workspace-acceptance, origin/feat/workspace-acceptance) fix: validate publish workspace JSON 33de3f1 feat: accept workspace publish requests 83d210b (origin/main, origin/HEAD, ...
M .github/workflows/release.yml M action.yml M docs/src/content/docs/targets/github.md A src/__tests__/action.test.ts M src/__tests__/config.test.ts M src/commands/__tests__/publish.test.ts M src/commands/publish.ts M src/config.ts M src/index.ts M src/schemas/project_config.ts M action.yml M docs/src/content/docs/targets/github.md M src/__tests__/action.test.ts M src/__tests__/config.test.ts M src/commands/__tests__/publish.test.ts M src/commands/publish.ts M src/config.ts M src/index.ts M src/schemas/project_config.ts M .github/workflows/release.yml M action.yml A src/__tests__/action.test.ts M src/commands/__tests__/publish.test.ts M src/commands/publish.ts
M .github/workflows/ci-poller.yml M .github/workflows/cocoapods-keepalive.yml M .github/workflows/publish.yml M README.md A docs/publish-issue-format.md M package.json M src/modules/__tests__/details-from-context.js A src/modules/__tests__/publish-workflow.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/update-issue.js M src/publish/inputs.js M yarn.lock M .github/workflows/ci-poller.yml M .github/workflows/publish.yml M docs/publish-issue-format.md M package.json M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-workflow.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/publish/inputs.js M yarn.lock M .github/workflows/cocoapods-keepalive.yml M .github/workflows/publish.yml M README.md A docs/publish-issue-format.md M src/modules/__tests__/details-from-context.js A src/modules/__tests__/publish-workflow.js M src/modules/__tests__/updat...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
1: name: 'Craft Prepare Release'
2: description: 'Prepare a new release using Craft'
3:
4: inputs:
5: version:
6: description: >
7: Version to release. Can be a semver string (e.g., "1.2.3"),
8: a bump type ("major", "minor", "patch"), or "auto" for automatic detection.
9: required: false
10: merge_target:
11: description: Target branch to merge into. Uses the default branch as a fallback. ...
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4e6c11e..323e8ba 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -12,6 +12,9 @@ on:
force:
description: Force a release even when there are release-blockers (optional)
required: false
+ workspace:
+ description: Named Craft release workspace to prepare and publish
+ required: false
# For external repos to call this workflow
workflow_call:
@@ -51,6 +54,10 @@ on:
type: string
required: false
default: '.'
+ workspace:
+ description: Named Craft release workspace to prepare and publish
+ type: string
+ required: false
craft_config_from_merge_target:
description: Use the craft config from the merge target branch
type: string
@@ -122,6 +129,7 @@ jobs:
with:
version: ${{ github.event.inputs.version }}
force: ${{ ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace.ts</path>
<type>file</type>
<content>
1: import { Argv, CommandBuilder } from 'yargs';
2:
3: import * as list from './workspace_cmds/list';
4:
5: export const command = ['workspace <command>'];
6: export const description = 'Manage release workspaces';
7:
8: export const builder: CommandBuilder = (yargs: Argv) =>
9: yargs.demandCommand().command(list);
10:
11: export const handler = (): void => {
12: /* pass */
13: };
(End of file - total 13 lines)
</content>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/list.ts</path>
<type>file</type>
<content>
1: import { getWorkspaceNames } from '../../config';
2: import { formatJson } from '../../utils/strings';
3:
4: export const command = ['list'];
5: export const description = 'List defined release workspaces as a JSON array';
6:
7: export function handler(): void {
8: console.log(formatJson(getWorkspaceNames()));
9: }
(End of file - total 9 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const CHECK_RUNS_LINK =
2: /^Requested by: @[^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Merge target: [^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Quick links:\r?\n(?:[ \t]*\r?\n)?[ \t]*- \[View changes\]\([^\r\n]+\)\r?\n[ \t]*- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)\r?$/m;
3: const CHECK_RUNS_LINK_COUNT = /^[ \t]*- \[View check runs\]\(/gm;
4:
5: function getReleaseRevision({ issueBody, repo }) {
6: if ((issueBody.match(CHECK_RUNS_LINK_COUNT) || []).length !== 1) {
7: throw new Error(
8: `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`
9: );
10: }
11:
12: const match = issueBody.match(CHECK_RUNS_LINK);
13: if (!match?.groups || match.groups.repo !== repo) {
14: throw new Error(
15: `Expected a View check runs link for ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/release-revision.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2:
3: const {
4: getReleaseRevision,
5: updateReleaseRevision,
6: } = require("../release-revision.js");
7:
8: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
9:
10: function requestBody(quickLinks) {
11: return `Requested by: @byk
12:
13: Merge target: (default)
14:
15: Quick links:
16: ${quickLinks}`;
17: }
18:
19: function canonicalQuickLinks(revision = REVISION) {
20: return `- [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
21: - [View check runs](https://github.com/getsentry/toolkit/commit/${revision}/checks/)`;
22: }
23:
24: test("gets the CI-approved revision from the request header", () => {
25: expect(
26: getReleaseRevision({
27: repo: "toolkit",
28: issueBody: requestBody(canonicalQuickLinks()),
29: })
30: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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: core.setOutput(
7: "revision",
8: getReleaseRevision({
9: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
10: repo,
11: })
12: );
13: }
14:
15: resolveReleaseRevision();
(End of file - total 15 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.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 ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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-workspace-acceptance/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2:
3: /**
4: * Matches the entire "Targets" section of a github publish issue body.
5: */
6: const TARGETS_SECTION_PARSER_REGEX =
7: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
8:
9: /**
10: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
11: * The "id" of the targets is captured within a capture group.
12: */
13: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
14:
15: /**
16: * Matches checked 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-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
1: /* eslint-disable */
2: // @generated by Peggy 5.1.0.
3: //
4: // https://peggyjs.org/
5:
6: "use strict";
7:
8: class peg$SyntaxError extends SyntaxError {
9: constructor(message, expected, found, location) {
10: super(message);
11: this.expected = expected;
12: this.found = found;
13: this.location = location;
14: this.name = "SyntaxError";
15: }
16:
17: format(sources) {
18: let str = "Error: " + this.message;
19: if (this.location) {
20: let src = null;
21: const st = sources.find((s) => s.source === this.location.source);
22: if (st) {
23: src = st.text.split(/\r\n|\n|\r/g);
24: }
25: const s = this.location.start;
26: const offset_s =
27: this.location.source &&
28: typeof this.location.source.offset === "function"
29: ? ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.peggy</path>
<type>file</type>
<content>
1: {
2: function join(characters) {
3: return characters.join("");
4: }
5: }
6:
7: // Canonical grammar for publish issue titles. A path suffix is syntactic only:
8: // the controller resolves a single segment as a workspace after checking out
9: // the CI-approved revision.
10: PublishIssueTitle
11: = "publish: " "getsentry/"? repo:Repository path:Path? workspace:LegacyWorkspace? "@" version:Version !. {
12: return {
13: repo,
14: path: path || "",
15: version,
16: ...(workspace ? { workspace } : {}),
17: };
18: }
19:
20: Repository
21: = characters:RepositoryCharacter+ { return join(characters); }
22:
23: RepositoryCharacter
24: = [A-Za-z0-9_.-]
25:
26: Path
27: = segments:("/" segment:PathSegment { return `/${segment}`; })+ { return join(segments); }
28:
29: PathSegment
30: = ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function needsWorkspaceDiscovery({ path, workspace }) {
2: return !workspace && /^\.\/[A-Za-z0-9_.-]+$/.test(path);
3: }
4:
5: function resolvePublishLocation({ path, workspace, workspaceNames }) {
6: if (workspace) {
7: if (path !== ".") {
8: throw new Error("A publish workspace must use the repository root path.");
9: }
10: return { path, workspace };
11: }
12:
13: if (!needsWorkspaceDiscovery({ path, workspace })) {
14: return { path };
15: }
16:
17: if (!workspaceNames.every(isWorkspaceName)) {
18: throw new Error(
19: "Craft workspace discovery returned an invalid workspace list."
20: );
21: }
22:
23: const segments = path.slice(2).split("/");
24: if (
25: path.startsWith("./") &&
26: segments.length === 1 &&
27: workspaceNames.includes(segments[0])
28: ) {
29: return { path: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-location.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const {
3: needsWorkspaceDiscovery,
4: resolvePublishLocation,
5: } = require("../modules/publish-location");
6:
7: function resolveLocation() {
8: const input = JSON.parse(process.env.PUBLISH_ARGS || "");
9: const workspaceNames = needsWorkspaceDiscovery(input)
10: ? JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "")
11: : [];
12:
13: if (!Array.isArray(workspaceNames)) {
14: throw new Error(
15: "Craft workspace discovery returned an invalid workspace list."
16: );
17: }
18:
19: core.setOutput(
20: "result",
21: resolvePublishLocation({
22: path: input.path,
23: workspace: input.workspace,
24: workspaceNames,
25: })
26: );
27: }
28:
29: resolveLocation();
(End of file - total 29 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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": ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const path = require("path");
3: const peggy = require("peggy");
4: const prettier = require("prettier");
5:
6: const grammarPath = path.join(
7: __dirname,
8: "..",
9: "src",
10: "modules",
11: "publish-issue-title.peggy"
12: );
13: const outputPath = path.join(
14: __dirname,
15: "..",
16: "src",
17: "modules",
18: "publish-issue-title.js"
19: );
20: const grammar = fs.readFileSync(grammarPath, "utf8");
21: const parser = peggy.generate(grammar, {
22: format: "commonjs",
23: grammarSource: "publish-issue-title.peggy",
24: output: "source",
25: });
26:
27: const generatedParser = prettier.format(`/* eslint-disable */\n${parser}`, {
28: filepath: outputPath,
29: });
30:
31: if (process.argv.includes("--check")) {
32: const currentParser = fs.readFileSync(outputPath, "utf8");
33: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/ci-poller.yml</path> <type>file</type> <content> 1: name: CI Status Poller 2: 3: on: 4: schedule: 5: - cron: "*/5 * * * *" 6: workflow_dispatch: 7: inputs: 8: attempt: 9: description: Self-dispatch attempt counter (internal use) 10: required: false 11: default: "0" 12: 13: permissions: 14: contents: read 15: issues: read 16: 17: jobs: 18: check-ci: 19: runs-on: ubuntu-latest 20: environment: production 21: # Skip entirely (no runner provisioned) when there's nothing to check. 22: # Set to "true" by ci-pending.yml, reset to "false" here when done. 23: # Always allow workflow_dispatch for manual recovery. 24: if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch' 25: concurrency: 26: group: ci-status-poller 27: cancel-in-progress: false 28: steps: 29: # sentry-internal-app token ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-ci-poller-input.js</path>
<type>file</type>
<content>
1: const { getCiPollerInput } = require("../modules/ci-poller-input");
2:
3: process.stdout.write(
4: JSON.stringify(
5: getCiPollerInput({
6: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
7: title: process.env.PUBLISH_TITLE || "",
8: revision: process.env.PUBLISH_REVISION || "",
9: })
10: )
11: );
(End of file - total 11 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/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:
7: function getCiPollerInput({ title, issueBody, revision }) {
8: let parsedTitle;
9: try {
10: parsedTitle = parsePublishIssueTitle(title);
11: } catch {
12: throw new Error(`Invalid publish issue title: '${title}'`);
13: }
14: const { repo, version } = parsedTitle;
15: const currentRevision = getReleaseRevision({ issueBody, repo });
16:
17: return {
18: ...(revision
19: ? { issueBody: updateReleaseRevision({ issueBody, repo, revision }) }
20: : {}),
21: repo: `getsentry/${repo}`,
22: revision: currentRevision,
23: version,
24: };
25: }
26:
27: module.exports = { getCiPollerInput };
(End of file - total 27 lines)
</content>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts</path> <type>file</type> <content> 1: /** 2: * Helpers for locating Craft's publish-state file in a path that is NOT 3: * writable by the repository being published. 4: * 5: * Background: `craft publish` writes a small JSON file listing targets 6: * that have completed so a resumed run can skip them. Before this module 7: * existed, the file lived at `.craft-publish-<version>.json` in the 8: * project's cwd. That path is inside the repository checkout, so any 9: * committed file at the same path (or any earlier CI step) could 10: * pre-populate the "published" set and trick Craft into silently 11: * skipping targets. 12: * 13: * The file now lives under `$XDG_STATE_HOME/craft/` (falling back to 14: * `$HOME/.local/state/craft/`). The filename is keyed on 15: * owner, repo, a hash of cwd (to disambiguate monorepo subpaths), and 16: * the version being published. ...
diff --git a/.github/workflows/ci-poller.yml b/.github/workflows/ci-poller.yml
index a3db430..324b63f 100644
--- a/.github/workflows/ci-poller.yml
+++ b/.github/workflows/ci-poller.yml
@@ -48,6 +48,11 @@ jobs:
private-key: ${{ secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY }}
owner: getsentry # create token with access to all getsentry repos
+ - name: Check out publish controller
+ uses: actions/checkout@v5
+ with:
+ path: .__publish__
+
- name: Check CI status for ci-pending issues
env:
# Use the sentry-internal-app token for label changes on this
@@ -92,26 +97,14 @@ jobs:
title=$(echo "$issue" | jq -r '.title')
body=$(echo "$issue" | jq -r '.body')
- # Parse repo and version from title: "publish: owner/repo[/path]@version"
- # Only take owner/repo (first two segments) — monorepos like
- # "getsentry/relay/py@0.9.26" have a path suffix that isn't part
- ...
33de3f1 feat: accept workspace publish requests .github/workflows/publish.yml | 27 +++- src/modules/__tests__/details-from-context.js | 212 +++++++++++++++++++++++++- src/modules/__tests__/publish-workflow.js | 82 ++++++++++ src/modules/__tests__/update-issue.js | 15 +- src/modules/details-from-context.js | 27 +++- src/modules/update-issue.js | 54 ++++--- 6 files changed, 377 insertions(+), 40 deletions(-) b658ffa fix: validate publish workspace JSON .github/workflows/cocoapods-keepalive.yml | 2 +- README.md | 5 ++ docs/publish-issue-format.md | 81 +++++++++++++++++++++++++++ src/modules/__tests__/details-from-context.js | 18 ++++++ src/modules/details-from-context.js | 8 ++- 5 files changed, 112 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-poller.yml b/.github/workflows/ci-poller.yml index a3db430..324b63f 100644 --- ...
yarn run v1.22.22 $ yarn check:generated && vitest run $ node scripts/generate-publish-issue-title-parser.js --check [1m[46m RUN [49m[22m [36mv4.1.0 [39m[90m/home/byk/Code/getsentry/publish-workspace-acceptance[39m [2m Test Files [22m [1m[32m9 passed[39m[22m[90m (9)[39m [2m Tests [22m [1m[32m50 passed[39m[22m[90m (50)[39m [2m Start at [22m 15:01:00 [2m Duration [22m 1.47s[2m (transform 438ms, setup 0ms, import 1.24s, tests 287ms, environment 1ms)[22m Done in 2.65s. yarn run v1.22.22 $ eslint src .github --ignore-pattern '!.github' Done in 0.90s.
> @sentry/craft@2.31.0-dev.0 test /home/byk/Code/getsentry/craft-workspace-action-propagation > vitest run RUN v4.1.8 /home/byk/Code/getsentry/craft-workspace-action-propagation [info] [[target/github]] No previous release found [info] [[target/github]] Deleted orphaned draft release: v1.0.0 [info] [[target/github]] No previous release found [warn] [[target/github]] Failed to clean up release "v1.0.0": Error: Cleanup failed [info] [[target/github]] No previous release found [info] [[target/github]] No previous release found [info] [[target/github]] Release for tag "1.0.0" already exists and is published. Skipping GitHub release creation (likely from a previous run). [info] [[target/github]] No previous release found [info] [[target/github]] createRelease returned 422 for tag "1.0.0". Looking for a leftover draft release to clean up... [info] [[target/github]] Deleting leftover draft release (id=789) for tag "1.0.0"... ...
M action.yml M docs/src/content/docs/targets/github.md M src/__tests__/action.test.ts M src/__tests__/config.test.ts M src/commands/__tests__/publish.test.ts M src/commands/publish.ts M src/config.ts M src/index.ts M src/schemas/project_config.ts ?? src/commands/workspace.ts ?? src/commands/workspace_cmds/ action.yml docs/src/content/docs/targets/github.md .github/workflows/release.yml src/commands/publish.ts src/commands/__tests__/publish.test.ts src/config.ts src/index.ts src/schemas/project_config.ts src/__tests__/action.test.ts src/__tests__/config.test.ts docs/pnpm-lock.yaml pnpm-lock.yaml
M .github/workflows/ci-poller.yml M .github/workflows/publish.yml M docs/publish-issue-format.md M package.json M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-workflow.js M src/modules/details-from-context.js M src/modules/post-workflow-details.js M src/publish/inputs.js M yarn.lock ?? scripts/ ?? src/modules/__tests__/ci-poller-input.js ?? src/modules/__tests__/publish-location.js ?? src/modules/__tests__/release-revision.js ?? src/modules/ci-poller-input.js ?? src/modules/publish-issue-title.js ?? src/modules/publish-issue-title.peggy ?? src/modules/publish-location.js ?? src/modules/release-revision.js ?? src/publish/resolve-ci-poller-input.js ?? src/publish/resolve-location.js ?? ...
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
publish-workspace-acceptance/src/modules/release-revision.js:2 — m makes the request-header regex match anywhere in the issue body. A complete forged Requested by / Merge target / Quick links block in a changelog can supply the sole recognized check-runs link and become the release revision. I reproduced this: a body beginning ### Changelog followed by a forged full block returns the attacker SHA. Anchor this parser to the body start and add that regression.publish-workspace-acceptance/.github/workflows/cocoapods-keepalive.yml:1 — unrelated quote-only rename is included in commit b658ffa. Drop it from this branch.src/modules/post-workflow-details.js and src/publish/inputs.js are excluded from intended staging.pnpm-lock.yaml. Craft’s pre-existing root and docs lockfiles are unchanged.release-revision.js:3-16.ci-poller.yml:100-107,143-147; manual workflow_dispatch remains: ci-poller.yml:3-12,24.details-from-context.js:34-59, Craft action.yml:74-87.publish.yml:138-149.publish.yml:159-167,239-249.publish.yml:217-235; Craft publishState.ts:82-99.package.json:18-20, scripts/generate-publish-issue-title-parser.js:31-37.yarn test (50) and lint; Craft pnpm test (1,156 passed), lint (7 existing warnings), and build.DO-NOT-MERGE
Date: Sep 2, 2026 * 🔴 [requested-review] (14:56) User requested a strict read-only final review of current working trees and full branch diffs in `/home/byk/Code/getsentry/craft-workspace-action-propagation` and `/home/byk/Code/getsentry/publish-workspace-acceptance`; explicitly instructed not to edit. * 🔴 (14:56) User specified final-review blockers to verify: release revision derives only fro…