Dashboard › craft › Session 0Td2fswmd7vY
0Td2fswmd7vYxddATPerform a strict READ-ONLY adversarial final audit. Do NOT edit any files, run formatters, write generated artifacts, commit, or change git state. Inspect BOTH worktrees and their complete uncommitted diffs against their current bases:
Context/contract:
craft workspace list; only an exact, case-sensitive, unnormalized complete suffix match selects CRAFT_WORKSPACE; nonmatches are checkout paths. Missing root .craft.yml means path behavior; discovery failure when root config exists fails closed.'', ., .., __proto__, leading -, non-ASCII/control) before target checkout and before state/publish effects. Workspace discovery output must obey the same policy.--config-from must resolve globs from the checked-out repository root rather than local config discovery. Validate paths/results before filesystem reads. Preserve exact workspace identities and valid normal globs (*, ?, character classes, globstar); do not normalize names.ci-ready labeled event for an open accepted issue without ci-pending/ci-failed. CI poller always adds ci-ready, and waiting-for-ci removes old ci-ready first.--rev.<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>
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/AGENTS.md</path> <type>file</type> <content> 1: # AGENTS.md - Coding Agent Guidelines 2: 3: This document provides guidelines for AI coding agents working in this repository. 4: 5: ## Project Overview 6: 7: This is the **Sentry Publish Repository** - a GitHub Actions-based approval system for publishing releases across multiple Sentry projects using [Craft](https://github.com/getsentry/craft). It's a pure JavaScript (Node.js 24.0.0) project using CommonJS modules. 8: 9: ## Build/Lint/Test Commands 10: 11: ### Package Manager 12: 13: - **Yarn 1.22.22** (classic) - Do NOT use npm 14: - Version management via Volta (pinned in package.json) 15: 16: ### Commands 17: 18: ```bash 19: # Install dependencies 20: yarn install 21: 22: # Run all tests 23: yarn test 24: 25: # Run tests in watch mode 26: yarn test:watch 27: 28: # Run a single test file 29: yarn test src/modules/__tests__/update-issue.js 30: 31: # Run tests ...
48620564f86d8724982cddb6506c040235fe8954 feat/workspace-action-propagation 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
52ef600d6759911d4b76d7f3108c5a41c3e0ec2a feat/workspace-acceptance 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/details-from-context.js M src/modules/post-workflow-details.js M src/modules/publish-issue-title.js M src/modules/publish-issue-title.peggy M src/modules/publish-location.js M src/publish/inputs.js M src/publish/resolve-location.js
...output truncated... Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_06b2de4a4001mykQwA10xLYtzf ::: :::note[Known limitation: the GitHub "Latest" badge is repo-wide] GitHub tracks a single "Latest" release **per repository**, not per tag prefix. Craft decides whether to mark a release as latest by comparing its version against the repository's current latest release ([`isLatestRelease`](https://github.com/getsentry/craft/blob/master/src/targets/github.ts)), which is not prefix-scoped. In a monorepo this means the "Latest" badge can move between products (e.g. publishing `cli@9.0.0` may take the badge from `mcp@3.0.0`, and publishing `cli@1.2.3` while `mcp@3.0.0` is latest won't earn the badge). Tags, changelogs, release branches, and version detection remain correctly per-product — only GitHub's single "Latest" pointer is shared. ...
...output truncated... Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_06b2e06580015xqPCtW6rEac3r `Quick links` must follow `Requested by` and `Merge target`, and contain exactly one `View changes` line followed by exactly one `View check runs` line for the checkout repository. -The controller and CI poller use that check-runs revision as the release authority. The -`accepted` label starts publishing; -`dry-run` requests dry-run mode. +The controller and CI poller use that check-runs revision as the release authority. +The `accepted` label starts CI waiting. Publishing starts only when the CI poller +adds a fresh `ci-ready` label to an open, accepted issue with neither `ci-pending` +nor `ci-failed`. `dry-run` requests dry-run mode. diff --git a/src/modules/__tests__/ci-poller-input.js b/src/modules/__tests__/ci-poller-input.js index 8c80856..77feb3a 100644 --- a/src/modules/__tests__/ci-poller-input.js +++ b/src/modules/__tests__/ci-poller-input.js @@ -1,53 ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/.github/workflows</path> <type>directory</type> <entries> auto-approve.yml ci-poller.yml cocoapods-keepalive.yml publish.yml test.yml (5 entries) </entries>
<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/config.ts</path>
<type>file</type>
<content>
1: import { existsSync, lstatSync, readFileSync } 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 './utils/version';
30: // ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/config.ts</path>
<type>file</type>
<content>
451:
452: /**
453: * Parses and validate passed configuration object
454: *
455: * Throw an error is the object cannot be properly parsed as configuration.
456: *
457: * @param rawConfig Raw project configuration object
458: */
459: export function validateConfiguration(
460: rawConfig: Record<string, any>,
461: ): CraftProjectConfig {
462: logger.debug('Parsing and validating the configuration file...');
463: try {
464: return CraftProjectConfigSchema.parse(rawConfig);
465: } catch (error) {
466: if (error instanceof ZodError) {
467: const messages = error.errors
468: .map(e => `${e.path.join('.')}: ${e.message}`)
469: .join('\n');
470: throw new ConfigurationError(
471: `Cannot parse configuration file:\n${messages}`,
472: );
473: }
474: throw error;
475: }
476: }
477:
478: /**
479: * Returns ...
<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/craft-workspace-action-propagation/src/__tests__/config.test.ts</path>
<type>file</type>
<content>
270: { name: 'github', tagPrefix: 'cli@' },
271: { name: 'github', tagPrefix: 'mcp@', id: 'second' },
272: ]);
273: expect(getGitTagPrefix()).toBe('cli@');
274: expect(warnSpy).toHaveBeenCalledTimes(1);
275: expect(warnSpy.mock.calls[0][0]).toMatch(/different "tagPrefix"/);
276: });
277:
278: test('warns when one github target has a prefix and another omits it', () => {
279: const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {});
280: loadWithTargets([
281: { name: 'github', tagPrefix: 'cli@' },
282: { name: 'github', id: 'second' },
283: ]);
284: // A mixed defined/undefined prefix is still ambiguous.
285: expect(getGitTagPrefix()).toBe('cli@');
286: expect(warnSpy).toHaveBeenCalledTimes(1);
287: });
288: });
289:
290: describe('workspaces', () => {
291: let ...
Found 23 matches /home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/config.test.ts: Line 12: loadConfigurationFromString, Line 18: getWorkspaceNames, Line 178: loadConfigurationFromString( Line 229: loadConfigurationFromString( Line 330: loadConfigurationFromString( Line 338: const config = loadConfigurationFromString(WS_CONFIG); Line 352: const config = loadConfigurationFromString(WS_CONFIG); Line 361: expect(() => loadConfigurationFromString(WS_CONFIG)).toThrow( Line 374: expect(getWorkspaceNames()).toEqual(['cli', 'mcp']); Line 380: expect(() => getWorkspaceNames()).toThrow( Line 401: expect(getWorkspaceNames()).toEqual(['packages/cli', 'packages/mcp']); Line 415: loadConfigurationFromString( Line 434: loadConfigurationFromString( Line 469: expect(() => getWorkspaceNames()).toThrow( Line 481: expect(() => ...
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/__tests__/prepare-dry-run.e2e.test.ts
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/package.json</path>
<type>file</type>
<content>
1: {
2: "name": "@sentry/craft",
3: "version": "2.31.0-dev.0",
4: "description": "The universal sentry workflow CLI",
5: "main": "dist/craft",
6: "repository": "https://github.com/getsentry/craft",
7: "author": "Sentry",
8: "license": "MIT",
9: "bin": {
10: "craft": "dist/craft",
11: "sentry-craft": "dist/craft"
12: },
13: "devDependencies": {
14: "@aws-sdk/client-lambda": "^3.984.0",
15: "@eslint/js": "^9.17.0",
16: "@google-cloud/storage": "^7.14.0",
17: "@octokit/plugin-retry": "^7.1.2",
18: "@octokit/request-error": "^7.0.0",
19: "@octokit/rest": "^21.0.2",
20: "@sentry/esbuild-plugin": "^2.23.0",
21: "@sentry/node": "^10.27.0",
22: "@types/async": "^3.0.1",
23: "@types/aws4": "^1.5.1",
24: "@types/cli-table": "^0.3.0",
25: "@types/git-url-parse": "^9.0.0",
26: "@types/is-ci": "^2.0.0",
27: ...
<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/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/.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/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 (path === ".") {
7: return { path };
8: }
9:
10: if (!workspaceNames.every(isWorkspaceName)) {
11: throw new Error(
12: "Craft workspace discovery returned an invalid workspace list."
13: );
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-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/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/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/craft-workspace-action-propagation/docs/src/content/docs/targets/github.md</path>
<type>file</type>
<content>
1: ---
2: title: GitHub
3: description: Create GitHub releases and tags
4: ---
5:
6: Creates a release on GitHub. If a Markdown changelog is present, this target reads the release name and description from it.
7:
8: ## Configuration
9:
10: | Option | Description |
11: |--------|-------------|
12: | `tagPrefix` | Prefix for new git tags (e.g., `v`). Empty by default. |
13: | `previewReleases` | Automatically detect and create preview releases. Default: `true` |
14: | `tagOnly` | Only create a tag (without a GitHub release). Default: `false` |
15: | `floatingTags` | List of floating tags to create/update. Supports `{major}`, `{minor}`, `{patch}` placeholders. |
16:
17: ## Environment Variables
18:
19: | Name | Description |
20: |------|-------------|
21: | `GITHUB_TOKEN` | Personal GitHub API token ([create ...
Found 25 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/__tests__/list.test.ts:
Line 4: getWorkspaceNames: vi.fn(),
Line 10: import { getWorkspaceNames } from '../../../config';
Line 13: describe('workspace list command', () => {
Line 15: vi.mocked(getWorkspaceNames).mockReturnValue(['cli', 'mcp.v2']);
Line 24: vi.mocked(getWorkspaceNames).mockReturnValue([]);
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/workspace_cmds/list.ts:
Line 1: import { getWorkspaceNames } from '../../config';
Line 8: console.log(formatJson(getWorkspaceNames()));
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/targets.ts:
Line 1: import { getConfiguration, expandWorkspaceTargets } from '../config';
Line 13: definedTargets = await expandWorkspaceTargets(definedTargets);
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/publish.ts:
Line 16: ...
<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>
Found 42 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/helpers.ts:
Line 26: * `CRAFT_WORKSPACE` env var) before yargs parsing.
Line 37: * back to `CRAFT_WORKSPACE`.
Line 40: * @param env The environment to read CRAFT_WORKSPACE from (defaults to process.env)
Line 46: const envWorkspace = env.CRAFT_WORKSPACE || undefined;
Line 60: // an earlier value selected; it falls back to CRAFT_WORKSPACE instead.
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/helpers.test.ts:
Line 65: test('CLI flag wins over CRAFT_WORKSPACE env', () => {
Line 68: CRAFT_WORKSPACE: 'mcp',
Line 86: { CRAFT_WORKSPACE: 'mcp' } as NodeJS.ProcessEnv,
Line 91: test('falls back to CRAFT_WORKSPACE when no flag', () => {
Line 94: CRAFT_WORKSPACE: 'mcp',
Line 103: CRAFT_WORKSPACE: 'mcp',
Line 117: CRAFT_WORKSPACE: 'cli',
Line 128: CRAFT_WORKSPACE: ...
<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/schemas/project_config.ts</path>
<type>file</type>
<content>
190: artifactProvider: BaseArtifactProviderSchema.optional(),
191: versioning: VersioningConfigSchema.optional(),
192: /**
193: * Do not merge the release branch after publishing.
194: * Defaults to true for compiled GitHub Actions (Node.js actions with dist/ folder).
195: */
196: noMerge: z.boolean().optional(),
197: } as const;
198:
199: /**
200: * Configuration for a single named workspace (release unit).
201: *
202: * A workspace mirrors the release-relevant subset of the top-level config;
203: * every field is optional and inherits the top-level value when omitted. The
204: * `github` block is *partial* (all fields optional) so a workspace can override
205: * `owner` and/or `repo` while inheriting the rest from the top-level `github`.
206: */
207: export const WorkspaceSchema = z.object({
208: ...releaseUnitFields,
209: ...
<path>/home/byk/Code/getsentry/craft-workspace-action-propagation/src/commands/prepare.ts</path>
<type>file</type>
<content>
780: );
781: return newVersion;
782: }
783:
784: // Explicit semver version - return as-is
785: return version;
786: }
787:
788: /**
789: * Body of 'prepare' command
790: *
791: * @param argv Command-line arguments
792: */
793: export async function prepareMain(argv: PrepareOptions): Promise<any> {
794: let git = await getGitClient();
795:
796: // Handle --config-from: load config from remote branch. The caller
797: // is responsible for ensuring the branch is trusted — the remote
798: // config's preReleaseCommand will be executed by Craft.
799: if (argv.configFrom) {
800: logger.warn(
801: `Loading .craft.yml from remote branch "${argv.configFrom}". ` +
802: `Its preReleaseCommand will be executed by Craft — ensure the ` +
803: `branch is trusted.`,
804: );
805: try {
806: await ...
<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 "-" ? ...
(no output)
(no output)
yarn run v1.22.22 $ node scripts/generate-publish-issue-title-parser.js --check Done in 0.45s.
Found 18 matches
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/publishState.ts:
Line 34: * `scripts/print-publish-state-path.ts` if present) that needs to
Line 71: * `publish-state-<owner>-<repo>-<sha1(cwd)[:12]>-<version>.json`
Line 76: * `publish-state-<sha256(cwd)[:16]>-<version>.json`
Line 90: ? `workspace-${Buffer.from(workspace).toString('base64url')}-`
Line 95: return `publish-state-${owner}-${repo}-${shortCwdHash(cwd)}-${workspacePrefix}${safeVersion}.json`;
Line 98: return `publish-state-${cwdDigest}-${workspacePrefix}${safeVersion}.json`;
/home/byk/Code/getsentry/craft-workspace-action-propagation/src/utils/__tests__/publishState.test.ts:
Line 52: /^publish-state-getsentry-craft-[0-9a-f]{12}-1\.2\.3\.json$/,
Line 84: expect(cli).toMatch(/-workspace-Y2xp-1\.2\.3\.json$/);
Line 85: expect(mcp).toMatch(/-workspace-bWNw-1\.2\.3\.json$/);
Line 107: ...
<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. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_06b2de4a4001mykQwA10xLYtzf</path>
<type>file</type>
<content>
1: diff --git a/action.yml b/action.yml
2: index 94f3158..84377a8 100644
3: --- a/action.yml
4: +++ b/action.yml
5: @@ -4,162 +4,162 @@ description: 'Prepare a new release using Craft'
6: inputs:
7: version:
8: description: >
9: Version to release. Can be a semver string (e.g., "1.2.3"),
10: a bump type ("major", "minor", "patch"), or "auto" for automatic detection.
11: required: false
12: merge_target:
13: description: Target branch to merge into. Uses the default branch as a fallback. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_06b2de4a4001mykQwA10xLYtzf</path>
<type>file</type>
<content>
1473: const configFilePath = findConfigFile();
1474: if (!configFilePath) {
1475: throw new ConfigurationError(
1476: `Cannot find Craft configuration file. Have you added "${CONFIG_FILE_NAME}" to your project?`,
1477: );
1478: }
1479: return configFilePath;
1480: }
1481:
1482: /**
1483: * Returns the path to the directory that contains the configuration file
1484: *
1485: * Returns "undefined" if no configuration file can be found.
1486: */
1487: export function getConfigFileDir(): string | undefined {
1488: const configFilePath = findConfigFile();
1489: if (!configFilePath) {
1490: return undefined;
1491: }
1492: return path.dirname(configFilePath);
1493: }
1494:
1495: /**
1496: * Parses and validate passed configuration object
1497: *
1498: * Throw an error is the object cannot be properly parsed as ...
<path>/home/byk/.local/share/opencode/tool-output/tool_06b2e06580015xqPCtW6rEac3r</path> <type>file</type> <content> 1: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml 2: index f6dc96a..c72068a 100644 3: --- a/.github/workflows/publish.yml 4: +++ b/.github/workflows/publish.yml 5: @@ -95,166 +95,161 @@ jobs: 6: run: | 7: gh workflow run ci-poller.yml -R "$GITHUB_REPOSITORY" 8: 9: publish: 10: runs-on: ubuntu-latest 11: environment: production 12: name: Publish a new version 13: # Publish when ci-ready is present (added by the poller after CI passes). 14: # Fires ONLY on ci-ready label events — not accepted — to avoid racing 15: # with waiting-for-ci on the same event. ...
<path>/home/byk/.local/share/opencode/tool-output/tool_06b2e06580015xqPCtW6rEac3r</path>
<type>file</type>
<content>
1576: s2 = peg$currPos;
1577: if (input.charCodeAt(peg$currPos) === 47) {
1578: s3 = peg$c3;
1579: peg$currPos++;
1580: } else {
1581: s3 = peg$FAILED;
1582: if (peg$silentFails === 0) {
1583: peg$fail(peg$e5);
1584: }
1585: }
1586: if (s3 !== peg$FAILED) {
1587: s4 = peg$parsePathSegment();
1588: if (s4 !== peg$FAILED) {
1589: peg$savedPos = s2;
1590: s2 = peg$f2(s4);
1591: } else {
1592: peg$currPos = s2;
1593: s2 = peg$FAILED;
1594: }
1595: } else {
1596: peg$currPos = s2;
1597: s2 = peg$FAILED;
1598: }
1599: if (s2 !== peg$FAILED) {
1600: while (s2 !== peg$FAILED) {
1601: s1.push(s2);
1602: s2 = peg$currPos;
1603: if (input.charCodeAt(peg$currPos) === 47) {
1604: ...
No files found
18972582fec2da2a7a0e202840b0b63fa928d5056eed83ac2eba978185c67ad7 -
e60910d6252339491d97efa5c106af690689fdfbafa8c8483f6cbfebbc14d7e4 -
<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:
4: /**
5: * Matches the entire "Targets" section of a github publish issue body.
6: */
7: const TARGETS_SECTION_PARSER_REGEX =
8: /^(?!### Targets$\s)(?: *- \[[ xX]\] \S+\s*$(?:\r?\n)?)+/m;
9:
10: /**
11: * Matches all targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
12: * The "id" of the targets is captured within a capture group.
13: */
14: const TARGETS_PARSER_REGEX = /^\s*- \[[ x]\] (\S+)/gim;
15:
16: /**
17: * Matches checked targets of a github publish issue body in a section that was already matched and extracted with `TARGETS_PARSER_REGEX`.
18: * The "id" of the targets is captured within a capture group. ...
<path>/home/byk/Code/getsentry/publish-workspace-acceptance/src/modules/__tests__/details-from-context.js</path>
<type>file</type>
<content>
90: };
91:
92: test("Do not extract merge_target value if its a default value", async () => {
93: const result = await detailsFromContext(defaultTargetInputsArgs);
94: expect(result).toStrictEqual({
95: dry_run: "",
96: merge_target: "",
97: path: ".",
98: repo: "sentry",
99: targets: ["github", "docker[latest]"],
100: version: "21.3.1",
101: });
102: });
103:
104: test("keeps a concrete workspace path in the title suffix", async () => {
105: const result = await detailsFromContext({
106: context: {
107: repo: { owner: "getsentry", repo: "publish" },
108: payload: {
109: issue: {
110: number: "123",
111: title: "publish: getsentry/toolkit/packages/cli@1.2.3",
112: body: "Requested by: @example",
113: labels: [],
114: },
115: },
116: ...
packages/[!a]* are rejected: both validators exclude ! and ^. This violates required character-class support. craft-workspace-action-propagation/src/schemas/project_config.ts:218-241, craft-workspace-action-propagation/src/config.ts:282-297.craft-workspace-action-propagation/docs/src/content/docs/targets/github.md:102-105, craft-workspace-action-propagation/src/config.ts:248-256.publish-workspace-acceptance/src/publish/inputs.js:1-12, publish-workspace-acceptance/src/modules/post-workflow-details.js:1-19.?, character-class, and globstar coverage. craft-workspace-action-propagation/src/__tests__/config.test.ts:407-424, craft-workspace-action-propagation/src/commands/prepare.ts:806-811.publish-workspace-acceptance/src/modules/publish-issue-title.peggy:11-18, publish-workspace-acceptance/src/modules/__tests__/details-from-context.js:104-140.GITHUB_REPOSITORY and the complete validated workspace path; path and workspace remain mutually exclusive. craft-workspace-action-propagation/action.yml:80-87, craft-workspace-action-propagation/action.yml:238-268, ...