Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sync-cli-release-dependencies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@fluojs/cli": patch
---

Refresh CLI starter dependency ranges after Changesets versioning so released scaffolds use the updated package versions.
52 changes: 52 additions & 0 deletions tooling/release/cli-published-dependencies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { afterEach, describe, expect, it } from 'vitest';
import { runVersionPackages } from './version-packages.mjs';

const repositoryRoot = fileURLToPath(new URL('../../', import.meta.url));
const releaseVersions: Readonly<Record<string, string>> = JSON.parse(readFileSync(
Expand Down Expand Up @@ -35,6 +36,57 @@ function createReleaseFixture() {
}

describe('CLI release metadata generation', () => {
it('refreshes metadata from manifests changed during versioning', () => {
const fixture = createReleaseFixture();
execFileSync(process.execPath, [fixture.script], { timeout: 10_000 });
const nextVersions = { ...releaseVersions, '@fluojs/config': '9.8.7', '@fluojs/react': '0.12.3' };

runVersionPackages({
workspacePackageManifests: () => [],
runChangesetsVersion: () => {
for (const [name, version] of Object.entries(nextVersions)) {
writeFileSync(
join(fixture.root, 'packages', name.slice('@fluojs/'.length), 'package.json'),
JSON.stringify({ name, version, type: 'module' }),
);
}
},
execFileSync: (command, args) => {
expect(command).toBe(process.execPath);
expect(args).toEqual([join(repositoryRoot, 'packages/cli/scripts/generate-published-internal-dependencies.mjs')]);
// Execute only the copied generator: its import.meta.url confines all writes to the fixture.
execFileSync(process.execPath, [fixture.script], { timeout: 10_000 });
},
});

const output = execFileSync(process.execPath, [
'--input-type=module', '--eval',
`import { PUBLISHED_INTERNAL_DEPENDENCIES } from ${JSON.stringify(pathToFileURL(fixture.output).href)}; console.log(JSON.stringify(PUBLISHED_INTERNAL_DEPENDENCIES));`,
], { encoding: 'utf8', timeout: 10_000 });
expect(JSON.parse(output)).toEqual(Object.fromEntries(
Object.entries(nextVersions).filter(([name]) => name !== '@fluojs/cli')
.map(([name, version]) => [name, `^${version}`]),
));
});

it('propagates generator failure after versioning without replacing metadata', () => {
const fixture = createReleaseFixture();
const previousOutput = readFileSync(fixture.output, 'utf8');

expect(() => runVersionPackages({
workspacePackageManifests: () => [],
runChangesetsVersion: () => {
writeFileSync(join(fixture.root, 'packages/config/package.json'), JSON.stringify({
name: '@fluojs/config', version: '',
}));
},
execFileSync: () => {
execFileSync(process.execPath, [fixture.script], { stdio: 'pipe', timeout: 10_000 });
},
})).toThrowError('Command failed:');
expect(readFileSync(fixture.output, 'utf8')).toBe(previousOutput);
});

it('refreshes stale metadata using independent release manifest versions', () => {
// Given: a fixture copies stale generated metadata before applying new release manifests.
const fixture = createReleaseFixture();
Expand Down
5 changes: 5 additions & 0 deletions tooling/release/version-packages.d.mts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export type ChangesetsVersionDependencies = {
};

export type VersionPackagesDependencies = {
readonly execFileSync?: (
command: string,
args: readonly string[],
options: { readonly stdio: 'inherit' },
) => unknown;
readonly existsSync?: (targetPath: string) => boolean;
readonly readFileSync?: (targetPath: string, encoding: 'utf8') => string;
readonly runChangesetsVersion?: (dependencies?: ChangesetsVersionDependencies) => void;
Expand Down
6 changes: 5 additions & 1 deletion tooling/release/version-packages.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { spawnSync } from 'node:child_process';
import { execFileSync, spawnSync } from 'node:child_process';
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
Expand Down Expand Up @@ -90,6 +90,7 @@ export function runChangesetsVersion(dependencies = {}) {

export function runVersionPackages(dependencies = {}) {
const {
execFileSync: executeFile = execFileSync,
existsSync: pathExists = existsSync,
readFileSync: readFile = readFileSync,
runChangesetsVersion: runVersion = runChangesetsVersion,
Expand All @@ -102,6 +103,9 @@ export function runVersionPackages(dependencies = {}) {
);

runVersion();
executeFile(process.execPath, [join(repoRoot, 'packages/cli/scripts/generate-published-internal-dependencies.mjs')], {
stdio: 'inherit',
});

const normalizedChangelogPaths = [];
const pendingWrites = [];
Expand Down
16 changes: 15 additions & 1 deletion tooling/release/version-packages.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { normalizePackageChangelog, runChangesetsVersion, runVersionPackages } from './version-packages.mjs';

describe('runChangesetsVersion', () => {
Expand Down Expand Up @@ -130,6 +130,7 @@ describe('runVersionPackages', () => {
const writes: string[] = [];

const result = runVersionPackages({
execFileSync: () => {},
existsSync: (targetPath) => changelogs.has(targetPath),
readFileSync: (targetPath) => {
const changelog = changelogs.get(targetPath);
Expand Down Expand Up @@ -187,6 +188,7 @@ describe('runVersionPackages', () => {

expect(() =>
runVersionPackages({
execFileSync: () => {},
existsSync: (targetPath) => changelogs.has(targetPath),
readFileSync: (targetPath) => {
const changelog = changelogs.get(targetPath);
Expand Down Expand Up @@ -225,4 +227,16 @@ describe('runVersionPackages', () => {
).toThrowError('Package CHANGELOG.md must contain at most one `## [Unreleased]` section.');
expect(writes).toEqual([]);
});

it('propagates versioning failure without running the CLI dependency generator', () => {
const failure = new Error('versioning failed');
const generate = vi.fn();

expect(() => runVersionPackages({
execFileSync: generate,
runChangesetsVersion: () => { throw failure; },
workspacePackageManifests: () => [],
})).toThrow(failure);
expect(generate).not.toHaveBeenCalled();
});
});