Dashboard › craft › Distillation
1cd25b49-42bd-48af-a7bf-cacad20198fa["lore_tm_v1_YelGjd_PxMWSnGdHs4FlMm5z3BsdzFrTowXfqsOyd8g","lore_tm_v1_f_Simr8xBLAr-7o37moxAEA0eEBkRmafRZvQc3nEZC8","lore_tm_v1_thzyWCC8xXRSZdMwErTuu77GwB9YWaa8y2SITuRIEJQ","lore_tm_v1_VDSFt0JlbIUxkzv2q3u6KWPRYkd9XX7oDF5d2bg4IAY","lore_tm_v1_8r3mZeIQwqeRMth13IPQLOz5vLVkiMHaj21Dj5XANS8","lore_tm_v1_MJ6dD9Ygh0lJEJ9CLUYRC94SsvRBxa1uO_z6YR9jCKY","lore_tm_v1_jihmTpwNdqg57eTpjyea_kbyu5xUBZrjd01PQp5tdmQ","lore_tm_v1_Fnm9sfRAymDVenHPZjXnVct-UAPerajKDcuvtZdzKGw","lore_tm_v1_nqx7nchcAqat5dI5Tpjn9HSRzfRKtUv1N0mXF0Rv3C0","lore_tm_v1_03fYjhcNoerWm-Zy_ADKg3tIhYGlyD-uJQ_TvHWfW3s","lore_tm_v1_gPib_IYKK5gcFG9jwtJxQnZztPykzT01H-8utrUfI0Y","lore_tm_v1_9DvWE0dly_TksPXScF0mqmVK3u54nQgMjNeX5A2LO-Q","lore_tm_v1_HMf4c1o_VlpRPjrJFdubnuy3F2pC8Urknmr4N3969Jo"]
Date: Aug 29, 2026
src/modules/publish-issue-title.peggy defines canonical PublishIssueTitle grammar: "publish: ", optional "getsentry/", repo:Repository, optional path:Path, optional legacy workspace:LegacyWorkspace, "@", and version:Version, followed by end-of-input. It returns { repo, path: path || "", version, ...(workspace ? { workspace } : {}) }.publish-issue-title.peggy treats a path suffix as syntactic only; controller-side logic must resolve a single path segment as a workspace only after checkout of the CI-approved revision.publish-issue-title.peggy RepositoryCharacter and PathSegment allow [A-Za-z0-9_.-]; Path joins one or more slash-prefixed path segments; Version allows [A-Za-z0-9_.+-]+.publish-issue-title.peggy supports legacy workspace syntax [workspace: <JsonString>], where JsonString preserves its quoted JSON form and permits non-control, non-quote, non-backslash characters plus JSON escapes (\", \\, \/, \b, \f, \n, \r, \t, and \u followed by four hex digits).src/modules/release-revision.js defines CHECK_RUNS_LINK as /^- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)$/m.getReleaseRevision({ issueBody, repo }) in src/modules/release-revision.js matches the issue bodyβs View check runs link, requires matched groups.repo === repo, throws Expected a View check runs link for getsentry/${repo} in the publish issue body. if absent/mismatched, and returns the matched 40-character lowercase hexadecimal commit revision.src/publish/resolve-release-revision.js parses process.env.PUBLISH_ARGS || "" for repo, calls getReleaseRevision({ issueBody: process.env.PUBLISH_ISSUE_BODY || "", repo }), and exposes it as the GitHub Actions output revision via core.setOutput("revision", ...).2.31.0; run craft workspace list only after checkout of the exact CI-approved revision; treat a missing root .craft.yml as path-only; and fail closed for any workspace-discovery error when configuration exists.craft-workspace-action-propagation/src/commands/publish.ts lines 640β679 show the no-rev flow: it calculates branchName = \${branchPrefix}/${newVersion}`, checks it out, and on failure calls findReleaseBranches(git, branchPrefix). It throws ConfigurationErrorcontaining the missing branch, suggestion to runcraft prepare, configured/default release branch prefix, exact and fuzzy branch matches where present, or No release branches found on the remote.`, plus the original error.src/commands/publish.ts lines 682β694 resolves revision = await git.revparse('HEAD'), checks CI status with checkRevisionStatus(statusProvider, revision, argv.noStatusCheck), prints artifact summary, and checks required artifacts. Lines 712 onward expands workspace targets before deriving the publish-state path..github/workflows/publish.yml failed because the expected Set target repo checkout branch block did not match the current file exactly; the failed expected block omitted the current stepβs env declarations for MERGE_TARGET and REPO..github/workflows/publish.yml parses issue inputs through node .__publish__/src/publish/inputs.js (id: inputs) and, on success, posts workflow details through node .__publish__/src/publish/post-workflow-details.js.Set target repo checkout branch (id: target-repo-branch), for specific repo/merge-target pairs: sentry-migr8/tmp-merge-target; sentry-javascript/v10, v9, v8, v7, or master; sentry-python/alpha; and sentry-wizard/1.x. It exports target_repo_branch=$MERGE_TARGET; comments require registered branches to be protected.actions/create-github-app-token@v3, vars.SENTRY_RELEASE_BOT_CLIENT_ID, secrets.SENTRY_RELEASE_BOT_PRIVATE_KEY, and owner: getsentry.getsentry/${{ fromJSON(steps.inputs.outputs.result).repo }} into __repo__ using actions/checkout@v6, token ${{ steps.token.outputs.token }}, fetch-depth: 0, and ref ${{ steps.target-repo-branch.outputs.target_repo_branch || '' }}βtherefore defaulting to the repository default branch when no allowlisted target branch applies.Set targets step runs only when parsed issue targets exist. It receives CRAFT_PUBLISH_REPO, CRAFT_PUBLISH_PATH, CRAFT_PUBLISH_VERSION, optional CRAFT_PUBLISH_WORKSPACE, and JSON targets; it generates {"published": ...} JSON using jq.Set targets script uses realpath -m "/github/workspace/__repo__/$CRAFT_PUBLISH_PATH" and rejects paths outside /github/workspace/__repo__; hashes the container cwd as first 12 characters of SHA-1; sanitizes owner/repo/version to lowercase [a-z0-9._-] tokens; base64url-encodes an optional workspace name; and writes state to $GITHUB_WORKSPACE/.craft-state/craft/publish-state-${owner_sanitised}-${repo_sanitised}-${cwd_hash}-${workspace_prefix}${version_sanitised}.json.docker://getsentry/craft:latest, changes directory to __repo__/${{ fromJSON(steps.inputs.outputs.result).path }}, and executes craft publish ${{ fromJSON(steps.inputs.outputs.result).version }}. It sets XDG_STATE_HOME: /github/workspace/.craft-state, optional CRAFT_WORKSPACE, CRAFT_MERGE_TARGET, and CRAFT_PUBLISH_STATE_GITHUB_REPO: getsentry/<parsed repo>.node .__publish__/src/publish/update-issue.js on cancellation or failure with PUBLISH_ARGS, CRAFT_STATE_FILE_PATH, and GITHUB_TOKEN; it separately posts cancellation with node .__publish__/src/publish/post-result.js cancelled.src/modules/details-from-context.js parses issue titles using parsePublishIssueTitle(), converts an optional legacy JSON workspace value with JSON.parse, and rejects invalid JSON with Invalid publish workspace JSON in title: '<title>'.detailsFromContext() rejects a legacy workspace that is empty or contains Unicode control (Cc), format (Cf), line separator (Zl), or paragraph separator (Zp) characters, with Workspace names must be nonempty and cannot contain Unicode control, format, or separator characters.detailsFromContext() derives path = "." + parsedTitleDetails.path, rejects any path whose slash-separated components include .., extracts merge_target from ^Merge target: (?<merge_target>[\w.\-/]+)$ in the issue body, and returns { ...parsedTitleDetails, dry_run, merge_target, path, targets, ...(workspace ? { workspace } : {}) }.detailsFromContext() determines dry_run as "1" when any issue label has name === "dry-run", otherwise ""; parses checked target IDs from the issue bodyβs targets section using TARGETS_SECTION_PARSER_REGEX and CHECKED_TARGETS_PARSER_REGEX.src/modules/__tests__/details-from-context.js verifies a View check runs link of the shape https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/ appears in representative publish issue bodies.details-from-context tests expect publish: getsentry/sentry@21.3.1 with selected targets github, npm[@sentry/node], and docker[latest] to parse as { dry_run: "", merge_target: "custom-branch", path: ".", repo: "sentry", targets: ["github", "npm[@sentry/node]", "docker[latest]"], version: "21.3.1" }.details-from-context tests verify version 4.2.6+sentry1, default merge-target parsing as merge_target: "", legacy title publish: getsentry/toolkit [workspace: "cli/v2"] @1.2.3 parsing to workspace cli/v2, and escaped legacy workspace syntax such as cli [preview] \"next\".craft-workspace-action-propagation/action.yml constructs publish issue titles as publish: ${GITHUB_REPOSITORY}${SUBDIRECTORY}${WORKSPACE:+/$WORKSPACE}@${RESOLVED_VERSION}; SUBDIRECTORY is /${inputs.path} unless inputs.path == '.', and WORKSPACE is appended as another slash segment.action.yml reads RELEASE_SHA from ${{ steps.craft.outputs.sha }}, derives issue metadata including RELEASE_BRANCH, RELEASE_PREVIOUS_TAG, MERGE_TARGET, PUBLISH_REPO, and WORKSPACE, and looks for an existing open issue by exact title using gh -R "$PUBLISH_REPO" issue list --json title,url,number,body filtered with jq.action.yml preserves checked publish targets from an existing issue by extracting only text between ### Targets and Checked targets will be skipped, storing checked target names in a Bash associative array, then applying [x] to matching targets in the rebuilt target list.action.yml truncates changelog content to MAX_CHANGELOG_CHARS=60000, appending \n\n---\n*Changelog truncated for issue body.* when exceeded, to remain under the approximately 65,536-character GitHub issue-body limit.publishMain(argv: PublishOptions) in craft-workspace-action-propagation/src/commands/publish.ts checks for dirty repository state unless argv.noGitChecks; it obtains branchPrefix from config.releaseBranchPrefix || DEFAULT_RELEASE_BRANCH_NAME.publishMain, when argv.rev is supplied, Craft calls git.raw('name-rev', '--name-only', '--no-undefined', rev), uses the resolved branch name or the supplied revision as checkoutTarget, logs Checking out revision, and runs await git.checkout(checkoutTarget). When argv.rev is absent, it instead follows the release-branch lookup/check-out flow.