Dashboard › craft › Session 0zg0KOV0iM0B
0zg0KOV0iM0BE974aPerform one final strict READ-ONLY adversarial audit of the CURRENT exact diffs and relevant source. Do not edit or modify files/git state.
Worktrees:
IMPORTANT: getsentry/craft:latest is intentional and REQUIRED. It must stay floating for the latest released Craft workspace behavior; target checkout SHA and craft publish --rev bind target operations. Do NOT report it.
The only prior MUST-FIX was nested malformed brace handling. Verify the correction precisely:
packages/{cli and packages/{cli} reject;packages/{cli,{mcp}} and nested empty packages/{cli,{{},mcp}} reject with no valid branch admitted;packages/{cli,{mcp,api}} expands correctly;Audit all previous required contracts for regressions: safe brace/character/negated-class/globstar and unsafe path/realpath containment; poller semantic identity/path validation before API calls; lossless identical Craft/Publish version-state names; action validation/ambient workspace/full title; revision checkout order/exact resolution/missing config fallback/fresh CI-ready/external state/--rev; parser-generated-doc test parity and scope hygiene.
Give only severity-ranked current file:line findings. Explicit PASS/CONCERN/MUST-FIX for each contract. End exactly MERGE or DO-NOT-MERGE. If blocked, start BLOCKED with exact reason. Never return empty.
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation</path> <type>directory</type> <entries> .craft.yml .dockerignore .eslintcache .git .github/ .gitignore .lore.md .nojekyll .npmignore .prettierignore .prettierrc.yml .tsbuildinfo .vscode/ action.yml AGENTS.md blog-post-draft.md build.mjs CHANGELOG.md CONTRIBUTING.md dist/ Dockerfile docs/ eslint.config.mjs img/ LICENSE node_modules/ package.json pnpm-lock.yaml README.md src/ tsconfig.build.json tsconfig.json vitest.config.mts (33 entries) </entries>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance</path> <type>directory</type> <entries> .eslintrc.js .git .github/ .gitignore AGENTS.md auto-approve-repos.txt docs/ LICENSE node_modules/ package.json README.md scripts/ src/ vitest.config.js yarn.lock (15 entries) </entries>
M action.yml M docs/src/content/docs/targets/github.md M src/__tests__/action.test.ts M src/__tests__/config.test.ts M src/commands/prepare.ts M src/config.ts M src/schemas/project_config.ts M src/utils/__tests__/publishState.test.ts M src/utils/publishState.ts feat/workspace-action-propagation 4862056 feat: support compact release workspaces 6dae341 feat: propagate release workspaces 21270a1 feat(config): add release workspaces 32f5462 fix(git): prevent fatal error for missing 'origin' remote (#871) 5d0ec43 fix(registry): retry registry push with backoff on concurrent updates (#870) 21270a1d12a60b461c51fba93bd076b90d9cf4af .github/workflows/release.yml | 9 + action.yml | 33 ++- docs/src/content/docs/targets/github.md | 36 ++- src/__tests__/action.test.ts | 248 +++++++++++++++++++++ src/__tests__/config.test.ts | 136 +++++++---- ...
M .github/workflows/publish.yml M docs/publish-issue-format.md M src/modules/__tests__/ci-poller-input.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/ci-poller-input.js M src/modules/details-from-context.js M src/modules/publish-issue-title.js M src/modules/publish-issue-title.peggy M src/modules/publish-location.js M src/publish/resolve-location.js ?? src/modules/publish-issue-validation.js feat/workspace-acceptance 52ef600 feat: resolve compact workspace publish requests b658ffa fix: validate publish workspace JSON 33de3f1 feat: accept workspace publish requests 83d210b build(deps): bump js-yaml from 4.3.0 to 4.3.1 (#9174) 5268a23 feat: Add Vercel creds fatal: Not a valid object name origin/master
origin https://github.com/getsentry/craft.git (fetch) origin https://github.com/getsentry/craft.git (push) byk/chore/prettier-reformat byk/fix/actions-node24-deprecations byk/fix/changelog-code-blocks byk/fix/changelog-commit-without-prerelease + byk/fix/commit-on-repo + byk/fix/dependabot-alerts-865 byk/fix/smol-toml-security + byk/pr865-fixes ci/fix-node20-deprecation-warnings dependabot/npm_and_yarn/docs/astro-6.1.6 dependabot/npm_and_yarn/tar-7.5.3 docs/improve-registry-target-docs docs/improve-registry-target-docs-v2 feat/actions-publish-repo-self + feat/ci-ready-signal feat/improve-onboarding feat/internal-changes-commit-types feat/npm-oidc-trusted-publishing feat/prefixed-tags feat/security-commit-prefix * feat/workspace-action-propagation + feat/workspaces-schema feat/workspaces-threading fix/action-artifact-name fix/bump-brace-expansion fix/calver-disable-publish-issue-mentions fix/changelog-preview-fork-prs ...
origin https://github.com/getsentry/publish.git (fetch) origin https://github.com/getsentry/publish.git (push) fatal: Needed a single revision chore/fix-security-advisories * feat/workspace-acceptance fix/add-workflow-permissions + main + opencode/tidy-orchid security/dual-write-publish-state security/remove-legacy-publish-state-write remotes/origin/HEAD -> origin/main remotes/origin/feat/ci-poller-variable-gate remotes/origin/feat/ci-status-poller remotes/origin/feat/workspace-acceptance remotes/origin/fix/ci-poller-gh-token remotes/origin/fix/ci-poller-variable-auth remotes/origin/fix/react-native-release-8-14-craft-config remotes/origin/main remotes/origin/revert-twine-verbose
83d210b29553ea6f4d97508821724a834c43856e .github/workflows/ci-poller.yml | 32 +- .github/workflows/cocoapods-keepalive.yml | 2 +- .github/workflows/publish.yml | 88 +-- README.md | 5 + docs/publish-issue-format.md | 86 +++ package.json | 5 +- scripts/generate-publish-issue-title-parser.js | 40 ++ src/modules/__tests__/ci-poller-input.js | 97 ++++ src/modules/__tests__/details-from-context.js | 131 ++++- src/modules/__tests__/publish-location.js | 91 ++++ src/modules/__tests__/publish-workflow.js | 153 ++++++ src/modules/__tests__/release-revision.js | 103 ++++ src/modules/__tests__/update-issue.js | 15 +- src/modules/ci-poller-input.js | 42 ++ src/modules/details-from-context.js | 31 +- src/modules/publish-issue-title.js | 725 +++++++++++++++++++++++++ ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
1: import { existsSync, lstatSync, readFileSync, realpathSync } from 'fs';
2: import path from 'path';
3: import { globSync, hasMagic } from 'glob';
4:
5: import { load } from 'js-yaml';
6: import GitUrlParse from 'git-url-parse';
7: import { createGitClient } from './utils/git';
8: import { ZodError } from 'zod';
9:
10: import { logger } from './logger';
11: import {
12: CraftProjectConfig,
13: CraftProjectConfigSchema,
14: GitHubGlobalConfig,
15: ArtifactProviderName,
16: StatusProviderName,
17: TargetConfig,
18: ChangelogPolicy,
19: VersioningPolicy,
20: Workspace,
21: } from './schemas/project_config';
22: import { ConfigurationError } from './utils/errors';
23: import { isCompiledGitHubAction } from './utils/detection';
24: import {
25: getPackageVersion,
26: parseVersion,
27: versionGreaterOrEqualThan,
28: SemVer,
29: } from ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/schemas/project_config.ts</path>
<type>file</type>
<content>
1: import { z } from 'zod';
2: import { hasMagic } from 'glob';
3:
4: /**
5: * DEPRECATED: Use changelog.policy instead. Different policies for changelog management
6: */
7: export const enum ChangelogPolicy {
8: Auto = 'auto',
9: Simple = 'simple',
10: None = 'none',
11: }
12:
13: /**
14: * Name of the status provider
15: */
16: export const enum StatusProviderName {
17: GitHub = 'github',
18: }
19:
20: /**
21: * Name of the artifact provider
22: */
23: export const enum ArtifactProviderName {
24: GCS = 'gcs',
25: GitHub = 'github',
26: None = 'none',
27: }
28:
29: /**
30: * Default versioning policy when no version argument is provided. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
1: import { describe, test, expect, vi, afterEach, beforeEach } from 'vitest';
2: import { mkdtempSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from 'fs';
3: import { tmpdir } from 'os';
4: import { join } from 'path';
5: /**
6: * Tests of our ability to read craft config files. (This is NOT general test
7: * configuration).
8: */
9:
10: import {
11: getGitTagPrefix,
12: loadConfigurationFromString,
13: validateConfiguration,
14: setActiveWorkspace,
15: getActiveWorkspace,
16: getConfiguration,
17: getVersioningPolicy,
18: getWorkspaceNames,
19: WORKSPACES_MIN_VERSION,
20: } from '../config';
21: import { CraftProjectConfigSchema } from '../schemas/project_config';
22: import { logger } from '../logger';
23:
24: describe('validateConfiguration', () => {
25: test('parses minimal configuration', () => {
26: const data = { ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-validation.js</path>
<type>file</type>
<content>
1: function isPublishRepository(name) {
2: return (
3: /^[A-Za-z0-9_.-]+$/.test(name) &&
4: name !== "." &&
5: name !== ".." &&
6: name !== "__proto__" &&
7: !name.startsWith("-")
8: );
9: }
10:
11: function isReleaseVersion(version) {
12: return /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-?([0-9a-z-]+(?:\.[0-9a-z-]+)*))?(?:\+([0-9a-z-]+(?:\.[0-9a-z-]+)*))?$/i.test(
13: version
14: );
15: }
16:
17: module.exports = { isPublishRepository, isReleaseVersion };
(End of file - total 17 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const {
3: getReleaseRevision,
4: updateReleaseRevision,
5: } = require("./release-revision");
6: const {
7: isPublishRepository,
8: isReleaseVersion,
9: } = require("./publish-issue-validation");
10: const { isPublishPath } = require("./publish-location");
11:
12: function getCiPollerInput({ title, issueBody, revision }) {
13: let parsedTitle;
14: try {
15: parsedTitle = parsePublishIssueTitle(title);
16: } catch {
17: throw new Error(`Invalid publish issue title: '${title}'`);
18: }
19: const { repo, version } = parsedTitle;
20: if (!isPublishRepository(repo)) {
21: throw new Error(`Invalid publish issue repository: '${repo}'`);
22: }
23: if (!isReleaseVersion(version)) {
24: throw new Error(`Invalid publish issue version: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/details-from-context.js</path>
<type>file</type>
<content>
1: const { parse: parsePublishIssueTitle } = require("./publish-issue-title");
2: const { isPublishPath } = require("./publish-location");
3: const {
4: isPublishRepository,
5: isReleaseVersion,
6: } = require("./publish-issue-validation");
7:
8: /**
9: * Matches the entire "Targets" section of a github publish issue body.
10: */
11: const TARGETS_SECTION_PARSER_REGEX =
12: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
13:
14: /**
15: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
16: * The "id" of the targets is captured within a capture group. ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-location.js</path>
<type>file</type>
<content>
1: function resolvePublishLocation({ path, workspaceNames }) {
2: if (!isPublishPath(path)) {
3: throw new Error("Invalid publish path.");
4: }
5:
6: if (!workspaceNames.every(isWorkspaceName)) {
7: throw new Error(
8: "Craft workspace discovery returned an invalid workspace list."
9: );
10: }
11:
12: if (path === ".") {
13: return { path };
14: }
15:
16: const workspace = path.slice(2);
17: if (workspaceNames.includes(workspace)) {
18: return { path: ".", workspace };
19: }
20:
21: return { path };
22: }
23:
24: function isPublishPath(path) {
25: return (
26: typeof path === "string" &&
27: (path === "." ||
28: (path.startsWith("./") &&
29: path
30: .slice(2)
31: .split("/")
32: .every(isSafeWorkspaceSegment)))
33: );
34: }
35:
36: function ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-location.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { resolvePublishLocation } = require("../modules/publish-location");
3:
4: function resolveLocation() {
5: const input = JSON.parse(process.env.PUBLISH_ARGS || "");
6: const workspaceNames = JSON.parse(process.env.CRAFT_WORKSPACE_NAMES || "");
7:
8: if (!Array.isArray(workspaceNames)) {
9: throw new Error(
10: "Craft workspace discovery returned an invalid workspace list."
11: );
12: }
13:
14: core.setOutput(
15: "result",
16: resolvePublishLocation({
17: path: input.path,
18: workspaceNames,
19: })
20: );
21: }
22:
23: resolveLocation();
(End of file - total 23 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.peggy</path>
<type>file</type>
<content>
1: {
2: function join(characters) {
3: return characters.join("");
4: }
5: }
6:
7: // Canonical grammar for publish issue titles. A path suffix is syntactic only:
8: // the controller resolves the complete suffix as a workspace after checking
9: // out the CI-approved revision.
10: PublishIssueTitle
11: = "publish: " "getsentry/"? repo:Repository path:Path? "@" version:Version !. {
12: return {
13: repo,
14: path: path || "",
15: version,
16: };
17: }
18:
19: Repository
20: = characters:RepositoryCharacter+ { return join(characters); }
21:
22: RepositoryCharacter
23: = [A-Za-z0-9_.-]
24:
25: Path
26: = segments:("/" segment:PathSegment { return `/${segment}`; })+ { return join(segments); }
27:
28: PathSegment
29: = characters:RepositoryCharacter+ { return join(characters); }
30:
31: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/publish-issue-title.js</path>
<type>file</type>
<content>
1: /* eslint-disable */
2: // @generated by Peggy 5.1.0.
3: //
4: // https://peggyjs.org/
5:
6: "use strict";
7:
8: class peg$SyntaxError extends SyntaxError {
9: constructor(message, expected, found, location) {
10: super(message);
11: this.expected = expected;
12: this.found = found;
13: this.location = location;
14: this.name = "SyntaxError";
15: }
16:
17: format(sources) {
18: let str = "Error: " + this.message;
19: if (this.location) {
20: let src = null;
21: const st = sources.find((s) => s.source === this.location.source);
22: if (st) {
23: src = st.text.split(/\r\n|\n|\r/g);
24: }
25: const s = this.location.start;
26: const offset_s =
27: this.location.source &&
28: typeof this.location.source.offset === "function"
29: ? ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const path = require("path");
3: const peggy = require("peggy");
4: const prettier = require("prettier");
5:
6: const grammarPath = path.join(
7: __dirname,
8: "..",
9: "src",
10: "modules",
11: "publish-issue-title.peggy"
12: );
13: const outputPath = path.join(
14: __dirname,
15: "..",
16: "src",
17: "modules",
18: "publish-issue-title.js"
19: );
20: const grammar = fs.readFileSync(grammarPath, "utf8");
21: const parser = peggy.generate(grammar, {
22: format: "commonjs",
23: grammarSource: "publish-issue-title.peggy",
24: output: "source",
25: });
26:
27: const generatedParser = prettier.format(`/* eslint-disable */\n${parser}`, {
28: filepath: outputPath,
29: });
30:
31: if (process.argv.includes("--check")) {
32: const currentParser = fs.readFileSync(outputPath, "utf8");
33: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "publish",
3: "private": true,
4: "version": "0.0.1",
5: "description": "Approval-based publishing system for Sentry",
6: "main": "index.js",
7: "repository": "git@github.com:getsentry/publish.git",
8: "author": "Sentry Open Source <oss@sentry.io>",
9: "license": "Apache-2.0",
10: "volta": {
11: "node": "24.0.0",
12: "yarn": "1.22.22"
13: },
14: "resolutions": {
15: "undici": "^6.23.0"
16: },
17: "scripts": {
18: "generate": "node scripts/generate-publish-issue-title-parser.js",
19: "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
20: "test": "yarn check:generated && vitest run",
21: "test:watch": "vitest",
22: "lint": "eslint src .github --ignore-pattern '!.github'",
23: "prettier": "prettier --write src"
24: },
25: "devDependencies": {
26: "eslint": ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/publish.yml</path>
<type>file</type>
<content>
1: name: Publish
2: on:
3: issues:
4: types: [labeled]
5:
6: concurrency:
7: # Use the issue title (e.g. "publish: getsentry/foo@1.2.3") so duplicate
8: # issues for the same repo@version share a concurrency group.
9: group: ${{ github.event.issue.title }}
10: cancel-in-progress: false
11:
12: permissions:
13: contents: read
14: issues: write
15: packages: write
16:
17: jobs:
18: # When accepted is added to a publish issue:
19: # - Add ci-pending (and remove ci-failed if retrying)
20: # - Enable the poller via CI_POLLER_HAS_PENDING=true
21: # - Comment on the issue
22: # - Trigger the poller immediately so we don't wait for the next cron tick
23: # The publish job below requires ci-ready, so it will not fire until the
24: # poller flips ci-pending → ci-ready (which also prevents publishing without
25: # CI ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows/ci-poller.yml</path> <type>file</type> <content> 1: name: CI Status Poller 2: 3: on: 4: schedule: 5: - cron: "*/5 * * * *" 6: workflow_dispatch: 7: inputs: 8: attempt: 9: description: Self-dispatch attempt counter (internal use) 10: required: false 11: default: "0" 12: 13: permissions: 14: contents: read 15: issues: read 16: 17: jobs: 18: check-ci: 19: runs-on: ubuntu-latest 20: environment: production 21: # Skip entirely (no runner provisioned) when there's nothing to check. 22: # Set to "true" by ci-pending.yml, reset to "false" here when done. 23: # Always allow workflow_dispatch for manual recovery. 24: if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch' 25: concurrency: 26: group: ci-status-poller 27: cancel-in-progress: false 28: steps: 29: # sentry-internal-app token ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/release-revision.js</path>
<type>file</type>
<content>
1: const CHECK_RUNS_LINK =
2: /^Requested by: @[^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Merge target: [^\r\n]+\r?\n(?:[ \t]*\r?\n)?[ \t]*Quick links:\r?\n(?:[ \t]*\r?\n)?[ \t]*- \[View changes\]\([^\r\n]+\)\r?\n[ \t]*- \[View check runs\]\(https:\/\/github\.com\/getsentry\/(?<repo>[A-Za-z0-9_.-]+)\/commit\/(?<revision>[0-9a-f]{40})\/checks\/?\)(?=\r?\n|$)/;
3: const CHECK_RUNS_LINK_COUNT = /^[ \t]*- \[View check runs\]\(/gm;
4:
5: function getReleaseRevision({ issueBody, repo }) {
6: if ((issueBody.match(CHECK_RUNS_LINK_COUNT) || []).length !== 1) {
7: throw new Error(
8: `Expected exactly one View check runs link in Quick links for getsentry/${repo}.`
9: );
10: }
11:
12: const match = issueBody.match(CHECK_RUNS_LINK);
13: if (!match?.groups || match.groups.repo !== repo) {
14: throw new Error(
15: `Expected a View check runs link ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts</path> <type>file</type> <content> 1: /** 2: * Helpers for locating Craft's publish-state file in a path that is NOT 3: * writable by the repository being published. 4: * 5: * Background: `craft publish` writes a small JSON file listing targets 6: * that have completed so a resumed run can skip them. Before this module 7: * existed, the file lived at `.craft-publish-<version>.json` in the 8: * project's cwd. That path is inside the repository checkout, so any 9: * committed file at the same path (or any earlier CI step) could 10: * pre-populate the "published" set and trick Craft into silently 11: * skipping targets. 12: * 13: * The file now lives under `$XDG_STATE_HOME/craft/` (falling back to 14: * `$HOME/.local/state/craft/`). The filename is keyed on 15: * owner, repo, a hash of cwd (to disambiguate monorepo subpaths), and 16: * the version being published. ...
Found 33 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts:
Line 89: export function getPublishStateFilename(
Line 118: export function getPublishStatePath(
Line 126: getPublishStateFilename(version, githubConfig, cwd, workspace),
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/publishState.test.ts:
Line 7: getPublishStateFilename,
Line 8: getPublishStatePath,
Line 42: describe('getPublishStateFilename', () => {
Line 46: const name = getPublishStateFilename(
Line 57: const a = getPublishStateFilename(
Line 62: const b = getPublishStateFilename(
Line 71: const cli = getPublishStateFilename(
Line 77: const mcp = getPublishStateFilename(
Line 91: getPublishStateFilename(
Line 104: getPublishStateFilename(version, { owner: 'o', repo: 'r' }, cwd),
Line 117: const name = getPublishStateFilename(
Line 126: ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
1: import { Arguments, Argv, CommandBuilder } from 'yargs';
2: import chalk from 'chalk';
3: import { existsSync, mkdirSync, readFileSync } from 'fs';
4:
5: import { safeFs } from '../utils/dryRun';
6: import { dirname, join } from 'path';
7: import * as shellQuote from 'shell-quote';
8: import stringLength from 'string-length';
9:
10: import {
11: getConfiguration,
12: getStatusProviderFromConfig,
13: getArtifactProviderFromConfig,
14: DEFAULT_RELEASE_BRANCH_NAME,
15: getGlobalGitHubConfig,
16: expandWorkspaceTargets,
17: getNoMergeConfig,
18: getActiveWorkspace,
19: } from '../config';
20: import { formatTable, logger } from '../logger';
21: import {
22: type GitHubGlobalConfig,
23: TargetConfig,
24: } from '../schemas/project_config';
25: import { getAllTargetNames, getTargetByName, SpecialTarget } from '../targets';
26: import { ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/inputs.js</path>
<type>file</type>
<content>
1: const github = require('@actions/github');
2: const core = require('@actions/core');
3: const { detailsFromContext } = require('../modules/details-from-context');
4:
5: async function inputs() {
6: const result = await detailsFromContext({
7: context: github.context,
8: });
9: core.setOutput('result', result);
10: }
11:
12:
13: inputs();
(End of file - total 13 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-release-revision.js</path>
<type>file</type>
<content>
1: const core = require("@actions/core");
2: const { getReleaseRevision } = require("../modules/release-revision");
3:
4: function resolveReleaseRevision() {
5: const { repo } = JSON.parse(process.env.PUBLISH_ARGS || "");
6: core.setOutput(
7: "revision",
8: getReleaseRevision({
9: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
10: repo,
11: })
12: );
13: }
14:
15: resolveReleaseRevision();
(End of file - total 15 lines)
</content>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/publish/resolve-ci-poller-input.js</path>
<type>file</type>
<content>
1: const { getCiPollerInput } = require("../modules/ci-poller-input");
2:
3: process.stdout.write(
4: JSON.stringify(
5: getCiPollerInput({
6: issueBody: process.env.PUBLISH_ISSUE_BODY || "",
7: title: process.env.PUBLISH_TITLE || "",
8: revision: process.env.PUBLISH_REVISION || "",
9: })
10: )
11: );
(End of file - total 11 lines)
</content>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/action.yml</path>
<type>file</type>
<content>
1: name: 'Craft Prepare Release'
2: description: 'Prepare a new release using Craft'
3:
4: inputs:
5: version:
6: description: >
7: Version to release. Can be a semver string (e.g., "1.2.3"),
8: a bump type ("major", "minor", "patch"), or "auto" for automatic detection.
9: required: false
10: merge_target:
11: description: Target branch to merge into. Uses the default branch as a fallback. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/action.test.ts</path>
<type>file</type>
<content>
1: import {
2: chmodSync,
3: mkdtempSync,
4: mkdirSync,
5: readFileSync,
6: rmSync,
7: writeFileSync,
8: } from 'fs';
9: import { tmpdir } from 'os';
10: import { join } from 'path';
11: import { spawnSync } from 'child_process';
12:
13: import { load } from 'js-yaml';
14: import { afterEach, expect, test } from 'vitest';
15:
16: interface ActionStep {
17: env?: Record<string, string>;
18: name?: string;
19: run?: string;
20: }
21:
22: function getActionSteps(): ActionStep[] {
23: const action = load(
24: readFileSync(join(__dirname, '../../action.yml'), 'utf8'),
25: ) as {
26: runs?: { steps?: ActionStep[] };
27: };
28: return action.runs?.steps || [];
29: }
30:
31: function getActionStep(name: string): ActionStep {
32: const step = getActionSteps().find(step => step.name === name);
33: if (!step?.run) {
34: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/docs/publish-issue-format.md</path>
<type>file</type>
<content>
1: # Publish Issue Format
2:
3: The Publish workflow treats a publish issue as a release request. The title identifies
4: the release; the body supplies the merge target and selected targets. Do not edit the
5: title by hand unless it remains valid under this format.
6:
7: ## Title
8:
9: Every title starts with `publish: `. This EBNF is canonical:
10:
11: ```text
12: title = "publish: ", [ "getsentry/" ], repository, [ path ],
13: "@", version ;
14: repository = token, { token } ;
15: path = "/", path-segment, { "/", path-segment } ;
16: path-segment = token, { token } ;
17: version = version-character, { version-character } ;
18: token = ? ASCII letter, digit, ".", "_", or "-" ? ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/ci-poller-input.js</path>
<type>file</type>
<content>
1: import { readFileSync } from "fs";
2: import { join } from "path";
3:
4: import { expect, test } from "vitest";
5:
6: const { getCiPollerInput } = require("../ci-poller-input.js");
7:
8: const REVISION = "7e5ca7ed5581552de066e2a8bc295b8306be38ac";
9: const issueBody = `Requested by: @byk
10:
11: Merge target: (default)
12:
13: Quick links:
14: - [View changes](https://github.com/getsentry/toolkit/compare/1.2.2...release/1.2.3)
15: - [View check runs](https://github.com/getsentry/toolkit/commit/${REVISION}/checks/)`;
16:
17: test("parses root and full-path publish titles", () => {
18: expect(
19: getCiPollerInput({
20: issueBody,
21: title: "publish: getsentry/toolkit/cli@1.2.3",
22: })
23: ).toEqual({
24: repo: "getsentry/toolkit",
25: revision: REVISION,
26: version: "1.2.3",
27: });
28:
29: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/details-from-context.js</path>
<type>file</type>
<content>
1: import { test, expect } from "vitest";
2:
3: const { detailsFromContext } = require("../details-from-context.js");
4:
5: const inputsArgs = {
6: context: {
7: repo: { owner: "getsentry", repo: "publish" },
8: payload: {
9: issue: {
10: number: "223",
11: title: "publish: getsentry/sentry@21.3.1",
12: body: `
13: Requested by: @BYK
14:
15: Merge target: custom-branch
16:
17: Quick links:
18: - [View changes](https://github.com/getsentry/sentry/compare/21.3.0...refs/heads/releases/21.3.1)
19: - [View check runs](https://github.com/getsentry/sentry/commit/7e5ca7ed5581552de066e2a8bc295b8306be38ac/checks/)
20:
21: Assign the **accepted** label to this issue to approve the release. ...
Found 13 matches
/home/byk/Code/getsentry/publish-workspace-acceptance/scripts/generate-publish-issue-title-parser.js:
Line 11: "publish-issue-title.peggy"
Line 18: "publish-issue-title.js"
Line 21: const parser = peggy.generate(grammar, {
Line 23: grammarSource: "publish-issue-title.peggy",
Line 27: const generatedParser = prettier.format(`/* eslint-disable */\n${parser}`, {
Line 35: "The generated publish issue title parser is stale. Run `yarn generate`."
/home/byk/Code/getsentry/publish-workspace-acceptance/package.json:
Line 18: "generate": "node scripts/generate-publish-issue-title-parser.js",
Line 19: "check:generated": "node scripts/generate-publish-issue-title-parser.js --check",
Line 20: "test": "yarn check:generated && vitest run",
/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/ci-poller-input.js:
Line 1: const { parse: parsePublishIssueTitle } = ...
Found 2 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/version.ts:
Line 37: export function isValidVersion(text: string): boolean {
Line 123: return isValidVersion(text) && !!text.match(PREVIEW_RELEASE_REGEX);
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/version.ts</path>
<type>file</type>
<content>
1: import { getGitTagPrefix } from '../config';
2:
3: /**
4: * Regular expression for matching semver versions.
5: *
6: * Modified to match version components
7: * Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
8: * @see https://github.com/sindresorhus/semver-regex
9: */
10: const semverRegex = () =>
11: /\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;
12:
13: /**
14: * Extracts a version number from the given text.
15: *
16: * In case the version contains a leading "v", it is stripped from the result.
17: * All semantic versions are supported. See {@link http://semver.org/} for
18: * more information. ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-location.js</path>
<type>file</type>
<content>
1: import { expect, test } from "vitest";
2:
3: const { resolvePublishLocation } = require("../publish-location.js");
4:
5: test("classifies an exact full workspace path without normalizing it", () => {
6: expect(
7: resolvePublishLocation({
8: path: "./packages/CLI",
9: workspaceNames: ["packages/cli", "packages/CLI"],
10: })
11: ).toStrictEqual({ path: ".", workspace: "packages/CLI" });
12: });
13:
14: test("keeps a non-workspace suffix as a checkout path", () => {
15: expect(
16: resolvePublishLocation({
17: path: "./packages",
18: workspaceNames: ["cli"],
19: })
20: ).toStrictEqual({ path: "./packages" });
21: });
22:
23: test("keeps a multi-segment suffix that is not an exact workspace path", () => {
24: expect(
25: resolvePublishLocation({
26: path: "./packages/cli",
27: ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/publish-workflow.js</path>
<type>file</type>
<content>
1: import { spawnSync } from "child_process";
2: import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
3: import { tmpdir } from "os";
4: import { join } from "path";
5:
6: import { afterEach, expect, test } from "vitest";
7:
8: const { resolvePublishLocation } = require("../publish-location.js");
9:
10: const temporaryDirectories = [];
11:
12: function getWorkflow() {
13: return readFileSync(
14: join(__dirname, "../../../.github/workflows/publish.yml"),
15: "utf8"
16: );
17: }
18:
19: function getSetTargetsScript() {
20: const workflow = getWorkflow();
21: const section = workflow.match(
22: / {6}- name: Set targets[\s\S]*? {8}run: \|\n(?<script>[\s\S]*?)\n {6}- uses: docker:\/\/getsentry\/craft:latest/
23: );
24: if (!section?.groups?.script) {
25: throw new Error("Missing Set targets workflow ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/publishState.test.ts</path>
<type>file</type>
<content>
1: import { describe, test, expect, beforeEach, afterEach } from 'vitest';
2: import { homedir } from 'os';
3: import { join } from 'path';
4:
5: import {
6: getCraftStateDir,
7: getPublishStateFilename,
8: getPublishStatePath,
9: } from '../publishState';
10:
11: describe('publishState', () => {
12: const savedEnv = { ...process.env };
13:
14: beforeEach(() => {
15: delete process.env.XDG_STATE_HOME;
16: });
17:
18: afterEach(() => {
19: process.env = { ...savedEnv };
20: });
21:
22: describe('getCraftStateDir', () => {
23: test('defaults to $HOME/.local/state/craft when XDG_STATE_HOME is unset', () => {
24: expect(getCraftStateDir()).toBe(
25: join(homedir(), '.local', 'state', 'craft'),
26: );
27: });
28:
29: test('honours XDG_STATE_HOME when set', () => {
30: ...
diff --git a/action.yml b/action.yml
index 94f3158..d160509 100644
--- a/action.yml
+++ b/action.yml
@@ -74,6 +74,7 @@ runs:
- name: Validate workspace
shell: bash
env:
+ LC_ALL: C
PATH_INPUT: ${{ inputs.path }}
WORKSPACE: ${{ inputs.workspace }}
run: |
@@ -81,8 +82,12 @@ runs:
echo "::error::The path and workspace inputs cannot be used together."
exit 1
fi
- if [[ -n "$WORKSPACE" ]] && ! node -e 'process.exit(/^(?!\.{1,2}$)(?!__proto__$)(?!-)[A-Za-z0-9_.-]+$/.test(process.env.WORKSPACE) ? 0 : 1)'; then
- echo "::error::Workspace names must use only ASCII letters, digits, periods, underscores, and hyphens."
+ if ! node -e 'const path = process.env.PATH_INPUT; const segment = /^[A-Za-z0-9_.-]+$/; process.exit(path === "." || (typeof path === "string" && path.split("/").every(name => segment.test(name) && ![".", "..", "__proto__"].includes(name) && !name.startsWith("-"))) ? ...
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index f6dc96a..23c546e 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -172,12 +172,7 @@ jobs:
PUBLISH_ARGS: ${{ steps.inputs.outputs.result }}
run: |
workspace_names='[]'
- requires_workspace_discovery="$(node -e '
- const { needsWorkspaceDiscovery } = require("./.__publish__/src/modules/publish-location");
- const input = JSON.parse(process.env.PUBLISH_ARGS || "");
- process.stdout.write(String(needsWorkspaceDiscovery(input)));
- ')"
- if [[ "$requires_workspace_discovery" == "true" && -f __repo__/.craft.yml ]]; then
+ if [[ -f __repo__/.craft.yml ]]; then
workspace_names="$(docker run --rm \
--volume "$GITHUB_WORKSPACE/__repo__:/github/workspace/__repo__" \
--workdir /github/workspace/__repo__ \
@@ -224,13 +219,18 @@ jobs:
...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/update-issue.js</path>
<type>file</type>
<content>
1: const fs = require("fs");
2: const {
3: TARGETS_SECTION_PARSER_REGEX,
4: TARGETS_PARSER_REGEX,
5: } = require("./details-from-context");
6:
7: async function updateTargets({
8: octokit,
9: stateFilePath,
10: publishRepo,
11: issue_number,
12: }) {
13: if (!stateFilePath || !fs.existsSync(stateFilePath)) {
14: return;
15: }
16:
17: const issueRequest = octokit.rest.issues.get({
18: ...publishRepo,
19: issue_number,
20: });
21:
22: const craftStateRequest = fs.promises
23: .readFile(stateFilePath, { encoding: "utf-8" })
24: .then((data) => JSON.parse(data));
25:
26: const [{ data: issue }, craftState] = await Promise.all([
27: issueRequest,
28: craftStateRequest,
29: ]);
30:
31: const newIssueBody = transformIssueBody(craftState, issue.body);
32:
33: await octokit.rest.issues.update({
34: ...
diff --git a/.github/workflows/ci-poller.yml b/.github/workflows/ci-poller.yml
index a3db430..324b63f 100644
--- a/.github/workflows/ci-poller.yml
+++ b/.github/workflows/ci-poller.yml
@@ -1,232 +1,226 @@
name: CI Status Poller
on:
schedule:
- cron: "*/5 * * * *"
workflow_dispatch:
inputs:
attempt:
description: Self-dispatch attempt counter (internal use)
required: false
default: "0"
permissions:
contents: read
issues: read
jobs:
check-ci:
runs-on: ubuntu-latest
environment: production
# Skip entirely (no runner provisioned) when there's nothing to check.
# Set to "true" by ci-pending.yml, reset to "false" here when done.
# Always allow workflow_dispatch for manual recovery.
if: vars.CI_POLLER_HAS_PENDING == 'true' || github.event_name == 'workflow_dispatch'
concurrency:
group: ci-status-poller
cancel-in-progress: false
steps:
# sentry-internal-app token ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/.github/workflows/release.yml</path>
<type>file</type>
<content>
1: name: Release
2: concurrency: ${{ github.workflow }}-${{ github.ref }}
3:
4: on:
5: # For Craft's own releases (dogfooding)
6: workflow_dispatch:
7: inputs:
8: version:
9: description: Version to release
10: required: true
11: default: 'auto'
12: force:
13: description: Force a release even when there are release-blockers (optional)
14: required: false
15: workspace:
16: description: Named Craft release workspace to prepare and publish
17: required: false
18:
19: # For external repos to call this workflow
20: workflow_call:
21: inputs:
22: version:
23: description: Version to release (semver, bump type, or "auto")
24: type: string
25: required: false
26: force:
27: description: Force a release even when there are ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/index.ts</path>
<type>file</type>
<content>
1: #!/usr/bin/env node
2: // Import Sentry instrumentation first, before any other code
3: import './instrument';
4:
5: import isCI from 'is-ci';
6: import yargs from 'yargs';
7:
8: import { logger, LogLevel } from './logger';
9: import {
10: sanitizeDynamicLinkerEnv,
11: warnIfCraftEnvFileExists,
12: } from './utils/env';
13: import {
14: envToBool,
15: setGlobals,
16: extractWorkspaceSelection,
17: } from './utils/helpers';
18: import { getPackageVersion } from './utils/version';
19: import { withTracing } from './utils/tracing';
20: import { setActiveWorkspace } from './config';
21:
22: // Commands
23: import * as prepare from './commands/prepare';
24: import * as publish from './commands/publish';
25: import * as targets from './commands/targets';
26: import * as config from './commands/config';
27: import * as artifacts from './commands/artifacts';
28: ...
Found 20 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts:
Line 42: export function extractWorkspaceSelection(
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/helpers.test.ts:
Line 10: extractWorkspaceSelection,
Line 37: describe('extractWorkspaceSelection', () => {
Line 42: extractWorkspaceSelection(['publish', '1.0.0'], NO_ENV),
Line 48: extractWorkspaceSelection(['publish', '--workspace', 'cli'], NO_ENV),
Line 53: expect(extractWorkspaceSelection(['--workspace=mcp'], NO_ENV)).toBe('mcp');
Line 57: expect(extractWorkspaceSelection(['--workspace=-cli'], NO_ENV)).toBe(
Line 61: extractWorkspaceSelection(['--workspace', '-cli'], NO_ENV),
Line 67: extractWorkspaceSelection(['--workspace', 'cli'], {
Line 75: extractWorkspaceSelection(
Line 84: extractWorkspaceSelection(
Line 93: extractWorkspaceSelection(['publish'], {
Line ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts</path>
<type>file</type>
<content>
1: import { appendFileSync, mkdirSync, writeFileSync } from 'fs';
2: import path from 'path';
3: import { parseArgs } from 'node:util';
4:
5: import prompts from 'prompts';
6: import { logger, LogLevel, setLevel } from '../logger';
7:
8: /**
9: * Maximum size (in bytes) for step output values passed through GITHUB_OUTPUT.
10: * Values exceeding this are truncated to avoid E2BIG errors when GitHub Actions
11: * expands them into environment variables for subsequent steps.
12: *
13: * 64 KB is well under the ~2 MB ARG_MAX kernel limit and also under GitHub's
14: * ~65 536-character issue body limit, so truncated changelogs still render. ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/list.ts</path>
<type>file</type>
<content>
1: import { getWorkspaceNames } from '../../config';
2: import { formatJson } from '../../utils/strings';
3:
4: export const command = ['list'];
5: export const description = 'List defined release workspaces as a JSON array';
6:
7: export function handler(): void {
8: console.log(formatJson(getWorkspaceNames()));
9: }
(End of file - total 9 lines)
</content>
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
740:
741: const calverFormat =
742: config.versioning?.calver?.format ?? DEFAULT_CALVER_CONFIG.format;
743:
744: return calculateCalVer(git, {
745: offset: calverOffset,
746: format: calverFormat,
747: });
748: }
749:
750: // Handle automatic version detection or version bump types
751: if (version === 'auto' || isBumpType(version)) {
752: if (!requiresMinVersion(AUTO_VERSION_MIN_VERSION)) {
753: const featureName = isBumpType(version)
754: ? 'Version bump types'
755: : 'Auto-versioning';
756: throw new ConfigurationError(
757: `${featureName} requires minVersion >= ${AUTO_VERSION_MIN_VERSION} in .craft.yml. ` +
758: 'Please update your configuration or specify the version explicitly.',
759: );
760: }
761:
762: const latestTag = await getLatestTag(git, ...
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cff6bc3..23c546e 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -118,151 +118,164 @@ jobs:
steps:
- name: Get repo contents
uses: actions/checkout@v6
with:
path: .__publish__
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 24
cache: yarn
cache-dependency-path: .__publish__/yarn.lock
- name: Install yarn dependencies
run: yarn install --cwd ".__publish__"
- name: Parse and set inputs
id: inputs
run: node .__publish__/src/publish/inputs.js
+ - name: Resolve CI-approved release revision
+ id: release-revision
+ env:
+ PUBLISH_ARGS: ${{ steps.inputs.outputs.result }}
+ PUBLISH_ISSUE_BODY: ${{ github.event.issue.body }}
+ run: node ...
Found 1 matches /home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts: Line 933: export async function getRevisionBranchName(
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts</path>
<type>file</type>
<content>
900: }
901: lines.push(
902: ``,
903: `Error: ${mergeError instanceof Error ? mergeError.message : String(mergeError)}`,
904: );
905: logger.warn(lines.join('\n'));
906: }
907:
908: // XXX(BYK): intentionally DO NOT await unlinking as we do not want
909: // to block (both in terms of waiting for IO and the success of the
910: // operation) finishing the publish flow on the removal of a temporary
911: // file. If unlinking fails, we honestly don't care, at least to fail
912: // the final steps. And it doesn't make sense to wait until this op
913: // finishes then as nothing relies on the removal of this file.
914: safeFs
915: .unlink(publishStateFile)
916: .catch((err: unknown) =>
917: logger.trace("Couldn't remove publish state file: ", err),
918: );
919: ...
diff --git a/src/commands/publish.ts b/src/commands/publish.ts
index 30bb834..dad43f3 100644
--- a/src/commands/publish.ts
+++ b/src/commands/publish.ts
@@ -1,71 +1,74 @@
import { Arguments, Argv, CommandBuilder } from 'yargs';
import chalk from 'chalk';
import { existsSync, mkdirSync, readFileSync } from 'fs';
import { safeFs } from '../utils/dryRun';
import { dirname, join } from 'path';
import * as shellQuote from 'shell-quote';
import stringLength from 'string-length';
import {
getConfiguration,
getStatusProviderFromConfig,
getArtifactProviderFromConfig,
DEFAULT_RELEASE_BRANCH_NAME,
getGlobalGitHubConfig,
expandWorkspaceTargets,
getNoMergeConfig,
getActiveWorkspace,
} from '../config';
import { formatTable, logger } from '../logger';
-import { TargetConfig } from '../schemas/project_config';
+import {
+ type GitHubGlobalConfig,
+ TargetConfig,
+} from '../schemas/project_config';
import { getAllTargetNames, getTargetByName, SpecialTarget } ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/__tests__/publish.test.ts</path>
<type>file</type>
<content>
1: import { vi, describe, test, expect, beforeEach, type Mock } from 'vitest';
2: import { join as pathJoin } from 'path';
3: import { spawnProcess, hasExecutable } from '../../utils/system';
4: import {
5: getPublishStateGitHubConfig,
6: getRevisionBranchName,
7: runPostReleaseCommand,
8: handleReleaseBranch,
9: MergeConflictError,
10: PushError,
11: } from '../publish';
12: import { getPublishStateFilename } from '../../utils/publishState';
13: import type { SimpleGit } from 'simple-git';
14:
15: vi.mock('../../utils/system');
16: vi.mock('../../utils/git', () => ({
17: getDefaultBranch: vi.fn().mockResolvedValue('main'),
18: getGitClient: vi.fn(),
19: isRepoDirty: vi.fn(),
20: findReleaseBranches: vi.fn(),
21: }));
22:
23: describe('runPostReleaseCommand', () => {
24: const newVersion = '2.3.4';
25: const ...
HEAD 48620564f86d8724982cddb6506c040235fe8954 base 21270a1d12a60b461c51fba93bd076b90d9cf4af working tree M action.yml M docs/src/content/docs/targets/github.md M src/__tests__/action.test.ts M src/__tests__/config.test.ts M src/commands/prepare.ts M src/config.ts M src/schemas/project_config.ts M src/utils/__tests__/publishState.test.ts M src/utils/publishState.ts diff sha256 c368e0271a7da6b04aec416162bfbf9dc3d2e8bdedd4716cb237903f71fefd6d - whitespace
HEAD 52ef600d6759911d4b76d7f3108c5a41c3e0ec2a base 83d210b29553ea6f4d97508821724a834c43856e working tree M .github/workflows/publish.yml M docs/publish-issue-format.md M src/modules/__tests__/ci-poller-input.js M src/modules/__tests__/details-from-context.js M src/modules/__tests__/publish-location.js M src/modules/__tests__/publish-workflow.js M src/modules/ci-poller-input.js M src/modules/details-from-context.js M src/modules/publish-issue-title.js M src/modules/publish-issue-title.peggy M src/modules/publish-location.js M src/publish/resolve-location.js ?? src/modules/publish-issue-validation.js diff sha256 8d150b5831c747629ff4c0765a4282ebe9e99393c4bc1a95ab0121ab3f143f8c - whitespace
MUST-FIX
publish/.github/workflows/cocoapods-keepalive.yml:1 has an unrelated quote-only change. Revert it.PASS
src/config.ts:330-390, src/schemas/project_config.ts:249-319; coverage: src/__tests__/config.test.ts:451-459,523-532.src/config.ts:231-328, src/schemas/project_config.ts:219-334.publish/src/modules/ci-poller-input.js:12-30, .github/workflows/ci-poller.yml:100-114.craft/src/utils/publishState.ts:51-105; publish/.github/workflows/publish.yml:217-233.craft/action.yml:74-92,191-224,275.ci-ready; Craft receives --rev: publish/.github/workflows/publish.yml:138-182,239-261; publish/src/modules/release-revision.js:1-27.publish/package.json:18-20, scripts/generate-publish-issue-title-parser.js:31-37, docs/publish-issue-format.md:9-47.DO-NOT-MERGE
Date: Sep 4, 2026 * 🟡 (23:03) User provided additional excerpt of `src/schemas/project_config.ts`, showing `StatusProvider` includes `Manual = 'manual'`; target configuration includes required `owner: z.string()` and optional `excludeNames: z.string().optional()`. * 🟡 (23:03) The `src/schemas/project_config.ts` excerpt defines `BaseStatusProviderSchema`, `BaseArtifactProviderSchema`, `GitHubArt…
Date: Sep 4, 2026 * 🔴 (23:03) User stated package-management preference: always use `pnpm`; never use `npm` or `yarn`. * 🔴 (23:03) User stated Node.js is managed by Volta, currently v22.12.0. * 🔴 (23:03) User stated dependencies must be installed with `pnpm install --frozen-lockfile`. * 🔴 (23:03) User stated tests use Vitest; test files are in `src/__tests__/`, follow the `*.test.ts` naming p…