Dashboard › craft › Distillation
d5cd8334-2323-4a4c-92d6-01034836e284["lore_tm_v1_g7i8VI_I8DpzyPQHIo8W5bdz9GQsBvufPLteAP4DWlU","lore_tm_v1_hTCCE1FQ3Ez3W6kTxioEvQ-NH6UfINKRpOW4JVz4gFM","lore_tm_v1_Vj1V189_iAbJfWCs28-PBioOnZrcIWMvbCSzRuESoa8","lore_tm_v1_30lWDADQUpsfGNOCVa2TCAwWZn_ARKDR4ThV-OYlPzw","lore_tm_v1_WwScTEMBtP5-pNxB1sVQ4uCWfMpiE3__EfkKxlPI75I","lore_tm_v1_L8OSQaK_TYLPKewG4cj4BYLl9NjJl8-dq102_iPMLr0","lore_tm_v1_CfiXs_UmVebgu_s2t_JTs5mabBrOcNDEloMWI7dT8Ok","lore_tm_v1_6HSTCNuZgm391HATxKvH6Tm4wI31oN0gFe8GS3DZnAg","lore_tm_v1_WzzxJG5w5upmZZvHWWUtcPPuqKdQXsSSeLJzSo-neAc","lore_tm_v1_Iy3dIlVjbSOLud-6KAe8G1Zm1xsb5AB7hV3siWqtAYs","lore_tm_v1_oBXoWgWSeulJWv7ZGkp6PdBJrzEj3XDbYBD99wlnZSs","lore_tm_v1_Q2-s19ttu8FF9-B1ylAQyxIwJb_A0zGbu8JODwVf8yw","lore_tm_v1_1-dQKx2zjrf8U-Jjth9oS1SW5HAJCvfLI878hHqBfNE","lore_tm_v1_Li5fmdHEmOF-uR5ePr9n_j1PQK0X7Xn6mEc071LxJjo","lore_tm_v1_9PXuXbUw-tWDa_FOVOWAn496aUhbThJvC2UAY1C_AbU","lore_tm_v1_nDcL2pcOAQM-EQiyaVVhcyeMrwdqhMbdB5NPydgCLis","lore_tm_v1_2367cgGR4qk6LYs6LzS_u8Vyj4F-qt6snf5ZhluOuog","lore_tm_v1_IGQ2lvuhbr2H6yQKiZgj0Dz2NbuXc3Chg7c9ZxfBYcY","lore_tm_v1_P5drdLrEegqiCyTlC5-ccZFj_VmK1x3CIzFpQwKEqaE","lore_tm_v1_g9pASX3zEBk0FCsLLcqjFMHz3VqJtv6s5jI7VXcQBRI","lore_tm_v1_Tr-yo-lH6OZLXO5QOnd6sSn4c2ufwC3ve5ncJf0KlF4"]
Date: Sep 8, 2026
.craft.yml always means checkout-path behavior.src/modules/details-from-context.js: detailsFromContext({ context }) validates issue context, parses context.payload.issue.title with parsePublishIssueTitle, validates repo via isPublishRepository, validates version via isReleaseVersion, prefixes parsed title path with ".", validates it via isPublishPath, obtains mergeTarget using getReleaseRevisionDetails({ issueBody, repo }), maps (default) to merge_target: "", sets dry_run: "1" only when a label has name === "dry-run", and extracts checked ### Targets entries using TARGETS_SECTION_PARSER_REGEX and CHECKED_TARGETS_PARSER_REGEX.src/modules/release-revision.js: isRevision(revision) permits only lowercase [0-9a-f]{40} SHAs; getReleaseRevisionDetails({ issueBody, repo }) requires parse(issueBody, { startRule: "CheckRunsLinkCount" }) === 1, parses with start rule "ReleaseRevision", verifies the parsed link repository equals repo, and reports errors referring to getsentry/${repo}. getReleaseRevision() returns .revision.value; updateReleaseRevision() replaces only the canonical parsed revision range (start/end) and rejects invalid replacements with "Release revision must be a lowercase 40-character SHA."src/publish/discover-location.js: getWorkspaceNames() returns [] when <repositoryDirectory>/.craft.yml is absent; otherwise executes docker run --rm --volume ${path.resolve(repositoryDirectory)}:/github/workspace/__repo__ --workdir /github/workspace/__repo__ getsentry/craft:latest workspace list, JSON-parses the output, and fails closed unless it is an array. discoverLocation() passes input.path and discovered names to resolvePublishLocation(). CLI main() defaults PUBLISH_ARGS to "{}", requires input.path, and defaults PUBLISH_REPOSITORY_DIRECTORY to "__repo__".src/publish/__tests__/discover-location.js specify: without root config, ./packages/cli remains { path: "./packages/cli" }; with root config and Craft output ["packages/CLI"], exact-case path ./packages/CLI resolves to { path: ".", workspace: "packages/CLI" }; Craft output "{}" or "\n" throws "Craft workspace discovery returned an invalid workspace list."src/modules/publish-location.js: resolvePublishLocation({ path, workspaceNames }) rejects invalid paths and invalid workspace lists; root path "." stays root; a non-root ./β¦ path resolves to { path: ".", workspace } only if its exact path.slice(2) appears in workspaceNames, otherwise remains { path }. Safe path/workspace segments match /^[A-Za-z0-9_.-]+$/, cannot be ., .., or __proto__, and cannot start with -.src/modules/publish-issue-validation.js: repository identities use the same safe token exclusions as workspace segments; isReleaseVersion() requires semantic major.minor.patch with no leading zeroes except zero itself, optional case-insensitive prerelease, and optional build metadata.docs/publish-issue-format.md defines title grammar from src/modules/publish-issue-title.peggy: publish: prefix; optional getsentry/; safe repository token; optional slash-separated safe path suffix; @ followed by [A-Za-z0-9_.+-]+ version. Examples are publish: getsentry/sentry@21.3.1 and publish: getsentry/toolkit/packages/cli@1.2.3; parsing continues to accept existing titles without getsentry/.View check runs link before resolving the entire title suffix; with root .craft.yml, craft workspace list supplies concrete workspace paths, exact matches become workspaces, all other suffixes remain checkout paths, and root-config discovery errors fail the release.scripts/generate-publish-issue-title-parser.js: generates CommonJS parser source from src/modules/publish-issue-title.peggy to src/modules/publish-issue-title.js using peggy.generate() with allowed start rules "PublishIssueTitle", "ReleaseRevision", and "CheckRunsLinkCount", grammarSource: "publish-issue-title.peggy", and output: "source"; formats via Prettier; also inserts the marked // BEGIN TITLE GRAMMAR/// END TITLE GRAMMAR excerpt into docs/publish-issue-format.md between <!-- BEGIN GENERATED TITLE GRAMMAR --> markers. --check detects stale parser or documentation and directs yarn generate.src/modules/__tests__/release-revision.js specifies canonical release-revision behavior: accepts CRLF bodies and /checks URLs without trailing slash; rejects absent checks path, links for another repository, decoy/embedded extra check-runs links, Quick links blocks outside the request header, and request headers outside the issue start; updates only the canonical request-header SHA; rejects uppercase, 39-character, and 41-character replacement SHAs.src/modules/__tests__/publish-workflow.js specifies workflow order: "Resolve CI-approved release revision" precedes "Inform start", "Check out target repo", "Resolve publish location", "Set targets", and "Publish using Craft"; checkout uses ref: ${{ steps.release-revision.outputs.revision }}; location discovery runs node .__publish__/src/publish/discover-location.js with PUBLISH_REPOSITORY_DIRECTORY: __repo__; Craft image must not be pinned to getsentry/craft:2.31.0; publishing runs craft publish ${{ fromJSON(steps.inputs.outputs.result).version }} --rev ${{ steps.release-revision.outputs.revision }}.'ci-ready' label event on an issue that is 'accepted' and 'ci-ready', and is neither 'ci-pending' nor 'ci-failed'. Root publish state filename uses .craft-state/craft/publish-state-getsentry-sentry-c232c383e26f-21.3.1.json; workspace packages/cli state filename uses .craft-state/craft/publish-state-getsentry-toolkit-c232c383e26f-workspace-cGFja2FnZXMvY2xp-1.2.3.json; case-distinct versions 4.2.6+sentry1 and 4.2.6+Sentry1 generate distinct base64-encoded -version-β¦json state files.src/modules/ci-poller-input.js: getCiPollerInput({ title, issueBody, revision }) parses and validates title repository, version, and path; obtains the current canonical release revision; when revision is supplied, returns issueBody updated by updateReleaseRevision(); returns repo: \getsentry/${repo}`, current revision, and version`.src/publish/inputs.js calls detailsFromContext({ context: github.context }) and writes JSON result through core.setOutput("result", ...); src/publish/resolve-ci-poller-input.js reads issue body from PUBLISH_ISSUE_BODY_FILE when set, otherwise PUBLISH_ISSUE_BODY, then writes getCiPollerInput() JSON to stdout; src/publish/resolve-release-revision.js parses PUBLISH_ARGS || "{}", requires repo, and sets output "revision" from PUBLISH_ISSUE_BODY.