Dashboard › craft › Distillation
c99332f8-ed88-4179-9d40-ba8daf4bec7d["lore_tm_v1_6aZWsZ6au-eGFdqg9vthscx27HsKn-2jUZcbpZ-lhH8","lore_tm_v1_tdVcG_Zr17MkgGJVsSmmFG00J7DUIFw7RWrnOeqcdcQ","lore_tm_v1_2rJLlBIgKIxO89e7mPO_YpVyFd0Ds1UDxrbs4FPUdW8","lore_tm_v1_7Zr5F01NnhSwDPox4djiiN7OrSxnFFaXHx2AZk5_q_0","lore_tm_v1_HseWbC9GT_E0ILPPnHj2jCAMBuSx1Lm0yt9m4ZCyaQE","lore_tm_v1_MTAC4T_K-ZYYt7TL08KEGzGAX4DtZtsvd4JKv-AE_UQ","lore_tm_v1_28nn_B_6c5M2cPDhc9uAl7FmJ2hP4s3pGWLGHoeeKVY","lore_tm_v1_G0m3SR3O84jhud2xR0sYLTDYQPJzzJd9fbhyW82FY48","lore_tm_v1_UQZMPyUYktPE_U1HfilJv7b5uQpBncu7XUEAwxocDak","lore_tm_v1_CbEW_KMlkls3sWePsx_by-fgK_O9gV3Zz2XgCOM2DWE","lore_tm_v1_JHrZ8I1W0wsZ3hMl9ZFx5b-QDNgROw9RMaEqwQ-1DNM"]
Date: Sep 4, 2026
craft-workspace-action-propagation: always use pnpm; never use npm or yarn.AGENTS.md output for /home/byk/Code/getsentry/craft-workspace-action-propagation specifies Node.js v22.12.0 managed by Volta; dependency installation command pnpm install --frozen-lockfile; development commands pnpm build, pnpm test, pnpm lint, and pnpm fix; and manual test command pnpm build && ./dist/craft.AGENTS.md output states the project uses TypeScript, Prettier 3.x with single quotes and no arrow parens (.prettierrc.yml), ESLint 9.x flat config in eslint.config.mjs using typescript-eslint, and permits unused variables prefixed _.AGENTS.md output states tests use Vitest, test files are in src/__tests__/ with *.test.ts names, and mocks use vi.fn(), vi.mock(), and vi.spyOn().AGENTS.md output states the main branch is master; CI tests Node.js 20 and 22; Craft dogfoods its own release tooling; and repository configuration is root .craft.yml with schema in src/schemas/.AGENTS.md output documents dry-run architecture: destructive Git operations must use getGitClient() from src/utils/git.ts or createGitClient(directory); GitHub API operations must use getGitHubClient() from src/utils/githubApi.ts; file writes use safeFs; other destructive commands use safeExec() or safeExecSync() from src/utils/dryRun.ts. Direct simple-git imports and new Octokit() are ESLint-restricted, with wrapper modules allowed an eslint-disable-next-line no-restricted-imports exception.src/utils/version.ts output shows semverRegex() is /\bv?(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([\da-z-]+(?:\.[\da-z-]+)*))?(?:\+([\da-z-]+(?:\.[\da-z-]+)*))?\b/gi; it accepts optional leading v, prerelease components, and build metadata.src/utils/version.ts output shows getVersion(text) extracts the first SemVer match and strips a leading v; isValidVersion(text) requires nonempty text exactly equal to getVersion(text).src/utils/version.ts output defines SemVer as { major: number; minor: number; patch: number; pre?: string; build?: string; }; parseVersion(text) extracts the first matching version into that structure.src/utils/version.ts output shows versionGreaterOrEqualThan(v1, v2) compares major, minor, patch, then stable releases above prereleases; it compares differing numeric-only prerelease strings lexically via v1.pre > v2.pre, throws if unresolved pre/build differences remain, and otherwise returns true.src/utils/version.ts output defines PREVIEW_RELEASE_REGEX as /(?:[^a-z])(preview|pre|rc|dev|alpha|beta|unstable|a|b)(?:[^a-z]|$)/i; isPreviewRelease(text) requires isValidVersion(text) plus a match to that regex.src/utils/version.ts output shows versionToTag(version, tagPrefix?) uses explicit tagPrefix or getGitTagPrefix() from ../config; getPackage() requires ../../package.json and throws Invalid package.json: the file is empty! if it has no keys; getPackageVersion() appends (${process.env.CRAFT_BUILD_SHA}) when CRAFT_BUILD_SHA is set; and semVerToString(s) serializes major.minor.patch, optional -pre, and optional +build.src/modules/ci-poller-input.js output shows getCiPollerInput({ title, issueBody, revision }) parses the title using parsePublishIssueTitle(title), maps parse failures to Invalid publish issue title: '${title}', validates repo with isPublishRepository() and version with isReleaseVersion(), derives path as .${parsedTitle.path}, and validates it with isPublishPath().src/modules/ci-poller-input.js output shows validation failures are Invalid publish issue repository: '${repo}', Invalid publish issue version: '${version}', or Invalid publish issue path: '${path}'; it obtains the current release revision via getReleaseRevision({ issueBody, repo }), returns repo: \getsentry/${repo}`, revision: currentRevision, and version, and conditionally returns issueBody: updateReleaseRevision({ issueBody, repo, revision })when a newrevision` is supplied.src/publish/resolve-ci-poller-input.js output shows it serializes getCiPollerInput() to stdout using PUBLISH_ISSUE_BODY, PUBLISH_TITLE, and PUBLISH_REVISION environment variables, each defaulting to "".ci-poller.yml workflow must always allow workflow_dispatch for manual recovery..github/workflows/ci-poller.yml output indicates it runs on cron schedule */5 * * * *, has a check-ci job with concurrency group ci-status-poller, and selects only issues carrying both ci-pending and accepted labels..github/workflows/ci-poller.yml output indicates its successful-CI path removes ci-pending, adds ci-ready, and comments CI checks passed for ${repo}@${version}. Publishing is starting now.; failure paths remove ci-pending, add failure status, cite the commit SHA and View check runs URL, and tell users to re-add accepted after CI is fixed to retry..github/workflows/ci-poller.yml output indicates it invokes node .__publish__/src/publish/resolve-ci-poller-input.js, triggers itself via gh workflow run ci-poller.yml -R "$GITHUB_REPOSITORY" -f attempt="${attempt}", and caps chained attempts at 60.ci-ready after CI passes..github/workflows/publish.yml output indicates the waiting-for-ci path handles accepted publish issues by adding ci-pending, removing ci-failed on retry, commenting that CI on the release branch is being checked, and setting repository variable CI_POLLER_HAS_PENDING to "true" using CI_POLLER_APP_PRIVATE_KEY..github/workflows/publish.yml output documents that publishing occurs when an open issue has ci-ready; this avoids racing the waiting-for-ci job on the same event, because the poller adds ci-ready after CI passes..github/workflows/publish.yml output indicates publishing checks out the release repository at steps.release-revision.outputs.revision, resolves Craft state and CRAFT_PUBLISH_WORKSPACE, validates that the container current working directory is /github/workspace/__repo__ or beneath it, and states that publish state follows the issue checkout identity rather than a workspace identity.src/publish/inputs.js output shows inputs() calls detailsFromContext({ context: github.context }) and writes the result to GitHub Actions output result via core.setOutput('result', result).src/publish/resolve-release-revision.js output shows resolveReleaseRevision() parses process.env.PUBLISH_ARGS || "" as JSON, extracts repo, resolves the revision from PUBLISH_ISSUE_BODY || "" with getReleaseRevision({ issueBody, repo }), and writes it as Actions output revision.src/modules/release-revision.js output shows CHECK_RUNS_LINK requires a Requested by, Merge target, and Quick links section containing a View changes link followed by exactly one View check runs link for https://github.com/getsentry/<repo>/commit/<40-lowercase-hex-revision>/checks; <repo> is restricted to [A-Za-z0-9_.-]+.src/modules/release-revision.js output shows getReleaseRevision({ issueBody, repo }) requires exactly one View check runs link, requires its repository to equal repo, throws specific errors if either condition fails, and returns the captured 40-character revision. updateReleaseRevision({ issueBody, repo, revision }) first validates and obtains the current revision, then replaces that revision within the matched link.