mirror of
https://github.com/davidkaya/aryx.git
synced 2026-07-23 21:18:40 +02:00
The release workflow tests hardcoded Windows-style backslash paths (C:\workspace\...) which caused path.relative() to produce incorrect results on Linux and macOS, where backslashes are not path separators. Replace hardcoded paths with resolve()/join() calls that produce platform-native paths, fixing CI failures on Linux and macOS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
227 lines
6.2 KiB
TypeScript
227 lines
6.2 KiB
TypeScript
import { join, resolve } from 'node:path';
|
|
import { describe, expect, test } from 'bun:test';
|
|
|
|
import {
|
|
incrementVersion,
|
|
parseReleaseBump,
|
|
runReleaseWorkflow,
|
|
type GitCommandResult,
|
|
type ReleaseDependencies,
|
|
} from '../../scripts/release';
|
|
|
|
const testRepositoryRoot = resolve('/test-workspace/aryx');
|
|
const testPackageJsonPath = join(testRepositoryRoot, 'package.json');
|
|
|
|
function gitSuccess(stdout = '', stderr = ''): GitCommandResult {
|
|
return {
|
|
exitCode: 0,
|
|
stdout,
|
|
stderr,
|
|
};
|
|
}
|
|
|
|
function gitFailure(exitCode = 1, stdout = '', stderr = ''): GitCommandResult {
|
|
return {
|
|
exitCode,
|
|
stdout,
|
|
stderr,
|
|
};
|
|
}
|
|
|
|
function createReleaseDependencies(options?: {
|
|
packageJsonText?: string;
|
|
gitResults?: GitCommandResult[];
|
|
}) {
|
|
let packageJsonText =
|
|
options?.packageJsonText ??
|
|
`{
|
|
"name": "aryx",
|
|
"version": "0.0.26"
|
|
}
|
|
`;
|
|
|
|
const gitResults = [...(options?.gitResults ?? [])];
|
|
const commands: string[][] = [];
|
|
const writes: Array<{ path: string; content: string }> = [];
|
|
const logs: string[] = [];
|
|
|
|
const dependencies: ReleaseDependencies = {
|
|
readText: async () => packageJsonText,
|
|
writeText: async (path, content) => {
|
|
packageJsonText = content;
|
|
writes.push({ path, content });
|
|
},
|
|
runGit: async (args) => {
|
|
commands.push(args);
|
|
const result = gitResults.shift();
|
|
|
|
if (!result) {
|
|
throw new Error(`Unexpected git command: ${args.join(' ')}`);
|
|
}
|
|
|
|
return result;
|
|
},
|
|
log: (message) => {
|
|
logs.push(message);
|
|
},
|
|
};
|
|
|
|
return {
|
|
dependencies,
|
|
commands,
|
|
writes,
|
|
logs,
|
|
getPackageJsonText: () => packageJsonText,
|
|
};
|
|
}
|
|
|
|
describe('release bump parsing', () => {
|
|
test('defaults to patch when no bump argument is provided', () => {
|
|
expect(parseReleaseBump()).toBe('patch');
|
|
});
|
|
|
|
test('rejects unsupported bump arguments', () => {
|
|
expect(() => parseReleaseBump('prerelease')).toThrow(
|
|
'Unsupported release bump "prerelease". Use patch, minor, or major.',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('version incrementing', () => {
|
|
test('supports patch, minor, and major bumps', () => {
|
|
expect(incrementVersion('0.0.26', 'patch')).toBe('0.0.27');
|
|
expect(incrementVersion('0.0.26', 'minor')).toBe('0.1.0');
|
|
expect(incrementVersion('0.0.26', 'major')).toBe('1.0.0');
|
|
});
|
|
});
|
|
|
|
describe('release workflow', () => {
|
|
test('bumps package.json, commits, tags, and pushes the tracked branch', async () => {
|
|
const { dependencies, commands, writes, logs, getPackageJsonText } = createReleaseDependencies({
|
|
gitResults: [
|
|
gitSuccess(),
|
|
gitSuccess('main\n'),
|
|
gitSuccess('origin\n'),
|
|
gitSuccess('refs/heads/main\n'),
|
|
gitSuccess(),
|
|
gitSuccess(),
|
|
gitSuccess(),
|
|
gitSuccess(),
|
|
gitSuccess(),
|
|
],
|
|
});
|
|
|
|
const result = await runReleaseWorkflow(
|
|
{
|
|
repositoryRoot: testRepositoryRoot,
|
|
packageJsonPath: testPackageJsonPath,
|
|
},
|
|
dependencies,
|
|
);
|
|
|
|
expect(result).toMatchObject({
|
|
bump: 'patch',
|
|
currentVersion: '0.0.26',
|
|
nextVersion: '0.0.27',
|
|
tagName: 'v0.0.27',
|
|
localBranch: 'main',
|
|
remote: 'origin',
|
|
upstreamBranch: 'main',
|
|
});
|
|
expect(writes).toHaveLength(1);
|
|
expect(getPackageJsonText()).toContain('"version": "0.0.27"');
|
|
expect(commands).toEqual([
|
|
['status', '--porcelain'],
|
|
['symbolic-ref', '--quiet', '--short', 'HEAD'],
|
|
['config', '--get', 'branch.main.remote'],
|
|
['config', '--get', 'branch.main.merge'],
|
|
['tag', '--list', 'v0.0.27'],
|
|
['add', 'package.json'],
|
|
['commit', '-m', 'chore: release v0.0.27'],
|
|
['tag', '-a', 'v0.0.27', '-m', 'Release v0.0.27'],
|
|
['push', '--follow-tags', 'origin', 'HEAD:main'],
|
|
]);
|
|
expect(logs).toEqual(['Released v0.0.27 from main to origin/main.']);
|
|
});
|
|
|
|
test('fails before writing package.json when the worktree is dirty', async () => {
|
|
const { dependencies, commands, writes } = createReleaseDependencies({
|
|
gitResults: [gitSuccess(' M README.md\n')],
|
|
});
|
|
|
|
await expect(
|
|
runReleaseWorkflow(
|
|
{
|
|
repositoryRoot: testRepositoryRoot,
|
|
packageJsonPath: testPackageJsonPath,
|
|
},
|
|
dependencies,
|
|
),
|
|
).rejects.toThrow('Release requires a clean git worktree. Commit, stash, or discard changes first.');
|
|
|
|
expect(writes).toHaveLength(0);
|
|
expect(commands).toEqual([['status', '--porcelain']]);
|
|
});
|
|
|
|
test('fails before writing package.json when the next release tag already exists', async () => {
|
|
const { dependencies, commands, writes } = createReleaseDependencies({
|
|
gitResults: [
|
|
gitSuccess(),
|
|
gitSuccess('main\n'),
|
|
gitSuccess('origin\n'),
|
|
gitSuccess('refs/heads/main\n'),
|
|
gitSuccess('v0.1.0\n'),
|
|
],
|
|
});
|
|
|
|
await expect(
|
|
runReleaseWorkflow(
|
|
{
|
|
repositoryRoot: testRepositoryRoot,
|
|
packageJsonPath: testPackageJsonPath,
|
|
bumpArg: 'minor',
|
|
},
|
|
dependencies,
|
|
),
|
|
).rejects.toThrow('Tag v0.1.0 already exists. Choose a different release version.');
|
|
|
|
expect(writes).toHaveLength(0);
|
|
expect(commands).toEqual([
|
|
['status', '--porcelain'],
|
|
['symbolic-ref', '--quiet', '--short', 'HEAD'],
|
|
['config', '--get', 'branch.main.remote'],
|
|
['config', '--get', 'branch.main.merge'],
|
|
['tag', '--list', 'v0.1.0'],
|
|
]);
|
|
});
|
|
|
|
test('fails when the current branch does not have an upstream', async () => {
|
|
const { dependencies, commands, writes } = createReleaseDependencies({
|
|
gitResults: [
|
|
gitSuccess(),
|
|
gitSuccess('release\n'),
|
|
gitFailure(),
|
|
gitFailure(),
|
|
],
|
|
});
|
|
|
|
await expect(
|
|
runReleaseWorkflow(
|
|
{
|
|
repositoryRoot: testRepositoryRoot,
|
|
packageJsonPath: testPackageJsonPath,
|
|
},
|
|
dependencies,
|
|
),
|
|
).rejects.toThrow('Release requires an upstream branch for release. Configure tracking before running the release helper.');
|
|
|
|
expect(writes).toHaveLength(0);
|
|
expect(commands).toEqual([
|
|
['status', '--porcelain'],
|
|
['symbolic-ref', '--quiet', '--short', 'HEAD'],
|
|
['config', '--get', 'branch.release.remote'],
|
|
['config', '--get', 'branch.release.merge'],
|
|
]);
|
|
});
|
|
});
|