Dashboard › craft › Distillation
cfaf71d4-6e53-40b8-85af-5f42f86c8022["lore_tm_v1_98yA2qrczajgV1hjqvSYH98Zt_GmbECRYrGfu7LokQQ","lore_tm_v1_u8GCKGRtlXnWKs2G9hRsMzs-k3N2meS6QICgQB9bDgQ"]
Date: Sep 8, 2026
.craft.yml always means checkout-path behavior.craft/src/commands/workspace_cmds/list.ts defines command = ['list'], description List defined release workspaces as a JSON array, and handler() prints formatJson(getWorkspaceNames()); craft/src/commands/workspace.ts exposes it through command = ['workspace <command>'], yargs.demandCommand().command(list), and a no-op handler.craft/src/config.ts:getPublishStateGitHubConfig(githubConfig, stateRepository = process.env.CRAFT_PUBLISH_STATE_GITHUB_REPO) deliberately overrides GitHub configuration only for publish-state identity because the Publish controllerβs checkout repository can differ from a workspace release GitHub configuration; it accepts owner/repo matching ^(?<owner>[A-Za-z0-9_.-]+)\/(?<repo>[A-Za-z0-9_.-]+)$, otherwise throws ConfigurationError('CRAFT_PUBLISH_STATE_GITHUB_REPO must be a GitHub owner/repository pair.')..github/workflows/publish.yml changed Resolve publish location to invoke node .__publish__/src/publish/discover-location.js with PUBLISH_ARGS: ${{ steps.inputs.outputs.result }} and PUBLISH_REPOSITORY_DIRECTORY: __repo__, replacing inline docker run ... getsentry/craft:latest workspace list plus resolve-location.js.actions/checkout@v6, named Check out target repo, using path: __repo__, ref: ${{ steps.release-revision.outputs.revision }}, repository: getsentry/${{ fromJSON(steps.inputs.outputs.result).repo }}, Release Bot token, and fetch-depth: 0.Set targets receives CRAFT_PUBLISH_REPO, resolved CRAFT_PUBLISH_PATH, CRAFT_PUBLISH_VERSION, resolved optional CRAFT_PUBLISH_WORKSPACE, and JSON targets; it creates a {"published": {...}} payload and documents safe state storage under $GITHUB_WORKSPACE/.craft-state, outside __repo__/, using a state identity based on Craftβs container cwd /github/workspace/__repo__[/subdir/...].docs/publish-issue-format.md now embeds a canonical Peggy title grammar generated from src/modules/publish-issue-title.peggy between <!-- BEGIN GENERATED TITLE GRAMMAR --> and <!-- END GENERATED TITLE GRAMMAR -->; valid examples are publish: getsentry/sentry@21.3.1 and publish: getsentry/toolkit/packages/cli@1.2.3.[A-Za-z0-9_.-]+ but reject ., .., __proto__, and leading -; repository identities follow the same safe-token rule and also reject those special values; valid Craft versions include build metadata such as 4.2.6+sentry1.View check runs revision, a complete title suffix that exactly matches a concrete craft workspace list path is a workspace; every other suffix is a checkout path; root .craft.yml discovery errors fail the release; getsentry/ remains optional for parsing existing issues.Merge target from optional to required; (default) denotes the target repository default branch, while explicit branches may contain letters, digits, _, ., /, and -.scripts/generate-publish-issue-title-parser.js now also generates docs/publish-issue-format.md grammar content. generateDocumentation({ documentation, titleGrammar }) replaces the marker-bounded grammar section and throws Could not find the generated title grammar in the documentation. if markers are absent.PublishIssueTitle, ReleaseRevision, and CheckRunsLinkCount; it extracts title grammar from // BEGIN TITLE GRAMMAR through // END TITLE GRAMMAR, throwing Could not find the publish issue title grammar. when absent.yarn generate --check behavior in scripts/generate-publish-issue-title-parser.js now checks both generated parser and documentation: stale parser throws The generated publish issue title parser is stale. Run \yarn generate`., and stale docs throw The generated publish issue title documentation is stale. Run `yarn generate`.; the module exports generateDocumentation`.src/modules/publish-issue-title.peggy adds ReleaseRevision, RequestHeader, CheckRunsRevision, and CheckRunsLinkCount parsing. The request header must begin with Requested by: @, then required Merge target: , Quick links:, one View changes link, and a View check runs URL in the form https://github.com/getsentry/<repo>/commit/<40 lowercase hex SHA>/checks[/].CheckRunsRevision accepts only exactly 40 lowercase [0-9a-f] characters and returns { value, start, end } source offsets; CheckRunsLinkCount counts every occurrence of - [View check runs]( across the complete issue body, protecting header parsing from appended decoy links.src/modules/publish-issue-title.js now exports StartRules: ["PublishIssueTitle", "ReleaseRevision", "CheckRunsLinkCount"] in addition to SyntaxError and parse.src/modules/release-revision.js replaced regex-based parsing with parse from ./publish-issue-title. getReleaseRevisionDetails({ issueBody, repo }) requires CheckRunsLinkCount === 1, parses ReleaseRevision, validates parsed repo equality, and retains existing errors: Expected exactly one View check runs link in Quick links for getsentry/${repo}. and Expected a View check runs link for getsentry/${repo} in the publish issue body.release-revision.js now exposes isRevision(revision) for lowercase 40-character SHA validation, getReleaseRevisionDetails, getReleaseRevision returning .revision.value, and updateReleaseRevision replacing exactly the parsed revision source range; invalid replacement revisions throw Release revision must be a lowercase 40-character SHA..src/modules/details-from-context.js now calls getReleaseRevisionDetails({ issueBody: context.payload.issue.body || "", repo: titleDetails.repo }) to parse required merge-target/header semantics; it returns merge_target: "" when mergeTarget === "(default)", otherwise the explicit merge target, replacing the former optional regex-derived merge target.src/publish/inputs.js now serializes the publish input output explicitly with core.setOutput('result', JSON.stringify(result)), rather than passing the result object directly.src/publish/resolve-ci-poller-input.js supports large/body-file input via PUBLISH_ISSUE_BODY_FILE: when set, it reads UTF-8 with readFileSync(process.env.PUBLISH_ISSUE_BODY_FILE, "utf8"); otherwise it uses PUBLISH_ISSUE_BODY || "".src/publish/resolve-location.js now defaults malformed/missing environment data safely to JSON.parse(process.env.PUBLISH_ARGS || "{}") and JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "[]"), preserves invalid-workspace-list error Craft workspace discovery returned an invalid workspace list., and JSON-stringifies resolvePublishLocation(...) before core.setOutput("result", ...).src/publish/resolve-release-revision.js now defaults to JSON.parse(process.env.PUBLISH_ARGS || "{}") and rejects absent repo with Publish input must define a repository. before resolving the issue-body revision..github/workflows/ci-poller.yml, .github/workflows/publish.yml, docs/publish-issue-format.md, scripts/generate-publish-issue-title-parser.js, src/modules/__tests__/details-from-context.js, src/modules/__tests__/publish-workflow.js, src/modules/__tests__/release-revision.js, src/modules/details-from-context.js, src/modules/publish-issue-title.js, src/modules/publish-issue-title.peggy, src/modules/release-revision.js, src/publish/inputs.js, src/publish/resolve-ci-poller-input.js, src/publish/resolve-location.js, and src/publish/resolve-release-revision.js; reported diff size is 1,005 insertions and 88 deletions.