Dashboard › craft › Distillation
25ce90d0-0035-4b8a-975a-37559c6fa62a["lore_tm_v1_t8xyH0R_Y2WXFsVr9Yvtmc-7_gNJmKLDJPLM5Wh8P8w","lore_tm_v1_tAt9XIqaaxqtf0bk51ibLS6pp-McwRLk84EP2TFKIT0","lore_tm_v1_gHIcAx5KfqiiHYs729kQBWbCR3qkaqmQNxmu9QySlug","lore_tm_v1_tmsLdN_2-DXvcqC4FMocVsZUpUJakNs6ZBIEhyzZVQI","lore_tm_v1_y_K3MSCCZKE9eFibCrRBspVBCthuZg6iq4UPwGhqgX0","lore_tm_v1_K08lGoEW_7he6a0JOwkvMM9IwuItDonTEVM95AqybJw","lore_tm_v1_blTxynhnHIarh38srXk6VUrZu7_5yYB8wOWi7W-lf5w","lore_tm_v1_C2LCtKyqreHcmIJD_CLB9hYQoGhu9iaRRvUIsWT6-CY","lore_tm_v1_4xZK9IehX2h4nekb-Vpozhv5Y0LN1hBS9mqSzMM8v1Y","lore_tm_v1_xm5hRhUxt4eMl5qwRcuNfXUCmQFxhMvC_RK0VLzCFnM","lore_tm_v1_X9BGbEpkRAMSwWjDGAUN4gL7cnUk9ilX9NiHHWX1skg","lore_tm_v1_94bU5Y-Q10NLbdsHT5aFhWVbHAThSCWVMoZPCIPPnvQ","lore_tm_v1_5d7QuzHf4omAptt1qrijNpqRzyJZquohixH03vhL0gI","lore_tm_v1_c1Z6jZOY4miVXjP0mZGvi5m4s-IHrTueTC6-MUUdh5I","lore_tm_v1_zP5y4qCYw9pGQqVyhGWOOU1L2QU0vNW4qkQb9AXwRcQ","lore_tm_v1_3DGHLnMPVItIi507ge-s-gSDRT0x6IcG6CnrUsf9Wgk"]
Date: Sep 8, 2026
.craft.yml always means checkout-path behavior.scripts/generate-publish-issue-title-parser.js generates src/modules/publish-issue-title.js from src/modules/publish-issue-title.peggy using Peggy with start rules PublishIssueTitle, ReleaseRevision, and CheckRunsLinkCount; formats the parser via Prettier and regenerates the // BEGIN TITLE GRAMMAR section in docs/publish-issue-format.md. With --check, it throws stale-parser or stale-documentation errors instructing yarn generate.src/modules/__tests__/generate-publish-issue-title-parser.js require exactly one well-formed <!-- BEGIN GENERATED TITLE GRAMMAR -->/<!-- END GENERATED TITLE GRAMMAR --> pair; missing, duplicate, or malformed markers throw Could not find the generated title grammar in the documentation.src/modules/release-revision.js from CHECK_RUNS_LINK/CHECK_RUNS_LINK_COUNT regexes to parse() from ./publish-issue-title. New getReleaseRevisionDetails({ issueBody, repo }) requires CheckRunsLinkCount === 1, parses ReleaseRevision, validates parsed details.repo === repo, and returns parsed header details; getReleaseRevision() returns revision.value.updateReleaseRevision({ issueBody, repo, revision }) now validates replacement revisions with isRevision() as lowercase [0-9a-f]{40}, throws Release revision must be a lowercase 40-character SHA. otherwise, and replaces only source offsets currentRevision.start through currentRevision.end, preserving every other byte of the issue body.src/modules/__tests__/release-revision.js cover canonical and CRLF bodies; accept check-runs URLs with or without trailing /; reject missing /checks, wrong repositories, decoy or embedded extra - [View check runs]( links anywhere in the body, Quick links outside the initial request header, and headers not at issue start. They verify revision-only replacement preserves CRLF and trailing content, and reject uppercase or 39-/41-character replacement SHAs.detailsFromContext() in src/modules/details-from-context.js now derives mergeTarget through getReleaseRevisionDetails({ issueBody: context.payload.issue.body || "", repo: titleDetails.repo }) rather than a standalone multiline Merge target regex; it maps (default) to merge_target: "", ignores later decoy merge-target lines, and retains title suffixes as paths prefixed with ..src/modules/__tests__/details-from-context.js verifies canonical headers produce repo, valid version, exact title-derived path, checked target IDs, and merge_target; accepts build metadata version 4.2.6+sentry1; rejects unexpected space before @version, escape/current-directory title segments (../, ./), unsafe repository identities -toolkit, ., .., __proto__, invalid versions --config and 1.2, version path syntax, and missing issue context.src/publish/resolve-ci-poller-input.js now reads issue body from PUBLISH_ISSUE_BODY_FILE using readFileSync(..., "utf8") when set, otherwise PUBLISH_ISSUE_BODY || "", before JSON-stringifying getCiPollerInput({ issueBody, title: PUBLISH_TITLE || "", revision: PUBLISH_REVISION || "" }).src/modules/__tests__/ci-poller-workflow.js simulate GitHub CLI and Node behavior for issue publish: getsentry/toolkit@1.2.3, initial SHA aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, and updated SHA bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb. They verify no edit or temp files when resolver fails, update output omits issueBody, or update output has empty issueBody; they also verify all body bytes are preserved outside revision rewriting.src/publish/inputs.js now serializes the detailsFromContext() result with JSON.stringify(result) before core.setOutput("result", ...), replacing passing the object directly.src/publish/resolve-location.js now defaults malformed/absent JSON environment input safely with PUBLISH_ARGS || "{}" and CRAFT_WORKSPACE_NAMES || "[]", continues to require CRAFT_WORKSPACE_NAMES be an array, and JSON-stringifies resolvePublishLocation() for GitHub Actions output. Its test verifies blank PUBLISH_ARGS fails with Invalid publish path. rather than Unexpected end of JSON input.src/publish/resolve-release-revision.js now defaults PUBLISH_ARGS to "{}" and explicitly throws Publish input must define a repository. when repo is absent; its test verifies blank PUBLISH_ARGS produces that validation error rather than a JSON parse error.src/publish/discover-location.js implements workspace discovery: if <repositoryDirectory>/.craft.yml is absent, getWorkspaceNames() returns []; otherwise it runs docker run --rm --volume ${path.resolve(repositoryDirectory)}:/github/workspace/__repo__ --workdir /github/workspace/__repo__ getsentry/craft:latest workspace list, parses JSON output, and requires an array or throws Craft workspace discovery returned an invalid workspace list.discoverLocation() resolves input.path using exact workspace names returned by Craft. main() requires input.path, defaults PUBLISH_REPOSITORY_DIRECTORY to "__repo__", and JSON-stringifies its result to core.setOutput("result", ...). Tests verify absent root configuration retains { path: "./packages/cli" }, exact-case ["packages/CLI"] maps ./packages/CLI to { path: ".", workspace: "packages/CLI" }, and invalid {} or empty newline Craft output fails closed.src/modules/publish-location.js resolves "." as { path: "." }; for a non-root valid path, it strips "./" and returns { path: ".", workspace } only if workspaceNames.includes(workspace) exactly, otherwise retains { path }. Valid path/workspace segments must match /^[A-Za-z0-9_.-]+$/, cannot be ., .., or __proto__, and cannot start with -.docs/publish-issue-format.md specifies canonical titles publish: getsentry/sentry@21.3.1 and publish: getsentry/toolkit/packages/cli@1.2.3; getsentry/ remains optional when parsing existing issues. It documents release authority as the CI-approved revision from the canonical View check runs URL, exact workspace discovery after checkout, failure on root-Craft discovery errors, and body requirements: Requested by, Merge target, Quick links, exactly one View changes line then exactly one checkout-repository View check-runs line, with accepted, ci-pending, ci-ready, ci-failed, and dry-run workflow-label semantics.