This document provides guidelines for AI coding agents working in this repository.
This is the Sentry Publish Repository - a GitHub Actions-based approval system for publishing releases across multiple Sentry projects using Craft. It's a pure JavaScript (Node.js 24.0.0) project using CommonJS modules.
- Yarn 1.22.22 (classic) - Do NOT use npm
- Version management via Volta (pinned in package.json)
# Install dependencies
yarn install
# Run all tests
yarn test
# Run tests in watch mode
yarn test:watch
# Run a single test file
yarn test src/modules/__tests__/update-issue.js
# Run tests matching a pattern
yarn test -t "pattern"
# Lint code
yarn lint
# Format code with Prettier
yarn prettier# By file path
yarn test src/modules/__tests__/update-issue.js
# By test name pattern (matches describe/test names)
yarn test -t "transformIssueBody"
# Combine file and pattern
yarn test src/modules/__tests__/update-issue.js -t "specific test name"src/
├── libs/ # Shared utilities
│ └── __tests__/
├── modules/ # Core business logic (reusable)
│ └── __tests__/
└── publish/ # GitHub Actions entry points
- libs/: Shared helper functions
- modules/: Core business logic, designed to be reusable
- publish/: Entry points called by GitHub Actions workflows
- Use kebab-case for all files:
details-from-context.js,update-issue.js - Test files go in
__tests__/subdirectory with same name as source file
- camelCase for variables and functions:
detailsFromContext,publishRepo - UPPER_SNAKE_CASE for constants/regex patterns:
TARGETS_SECTION_PARSER_REGEX
- CommonJS for all source files (
require/module.exports) - ES Modules for test files (
import/export)
- Node.js built-ins (
fs,path) - External packages (
@actions/github,@actions/core) - Local modules (relative paths)
// Example - source file
const fs = require("fs");
const github = require("@actions/github");
const core = require("@actions/core");
const { detailsFromContext } = require("../modules/details-from-context");// Example - test file
import { vi, describe, test, expect } from "vitest";
import fs from "fs";
const { updateIssue } = require("../update-issue.js");Use CommonJS exports:
// Named exports (preferred for multiple exports)
module.exports = { functionA, functionB };
// Single default export
module.exports = functionName;- Prettier with default settings
- Double quotes for strings
- Semicolons required
- Run
yarn prettierbefore committing
Include context about what went wrong and potential fixes:
throw new Error(
'No "GITHUB_TOKEN" environment variable found. ' +
"Please ensure the workflow is configured correctly"
);Validate inputs early with descriptive error messages:
if (!context || !context.payload || !context.payload.issue) {
throw new Error("Issue context is not defined");
}Always throw for unexpected values:
switch (status) {
case "failure":
return {
/* ... */
};
case "cancelled":
return {
/* ... */
};
case "success":
return {
/* ... */
};
default:
throw new Error(`Unknown status: '${status}'`);
}Before reading files:
if (!fs.existsSync(CRAFT_STATE_FILE_PATH)) {
return;
}- Vitest v4.0.0 with globals enabled
- No need to import
describe,test,expectin test files (they're global) - Mocks are auto-cleared between tests
import { vi, describe, test, expect } from "vitest";
// Mock external dependencies
vi.mock("@actions/github");
vi.mock("@actions/core");
// Import module under test AFTER mocks
const { myFunction } = require("../my-module.js");
describe("myFunction", () => {
test("does something", () => {
expect(myFunction()).toBe(expected);
});
});// Mock a module
vi.mock("@actions/github");
// Mock with implementation
vi.mock("fs", () => ({
existsSync: vi.fn(),
readFileSync: vi.fn(),
}));
// Inline snapshots for complex output
expect(result).toMatchInlineSnapshot(`"expected output"`);@actions/core- GitHub Actions toolkit (inputs, outputs, logging)@actions/github- GitHub API client for Actions@sentry/node- Sentry error reporting
await Promise.all([operation1(), operation2()]);const { repo, owner, issueNumber } = context;const fullContext = { ...publishRepo, additionalProp: value };const message = `Release ${version} published successfully`;const PARSER_REGEX = /(?<name>\w+): (?<value>.+)/;
const match = text.match(PARSER_REGEX);
const { name, value } = match.groups;This codebase interacts heavily with GitHub Actions. Entry points in src/publish/ are called by workflows and use:
@actions/corefor inputs/outputs@actions/githubfor GitHub API interactions- Issue-based workflows triggered by labels and comments
All code is owned by @getsentry/releng (Release Engineering team).