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
7 changes: 7 additions & 0 deletions packages/create-sei/scripts/pending-changesets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* `.changeset` stores `config.json` and `README.md` next to the changesets themselves, so
* only the remaining markdown entries describe releases that Changesets has yet to consume.
*/
export function hasPendingChangesets(changesetDirectoryEntries: string[]): boolean {
return changesetDirectoryEntries.some((entry) => entry.endsWith('.md') && entry !== 'README.md');
}
26 changes: 23 additions & 3 deletions packages/create-sei/scripts/smoke-generated-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { promises as fs } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { BRAND_ASSET_HASHES } from '../brand-assets';
import { hasPendingChangesets } from './pending-changesets';
import { type PrecompilesSource, type PrecompilesSourceSelection, type RequestedPrecompilesSource, selectPrecompilesSource } from './select-precompiles-source';

const packageRoot = path.resolve(import.meta.dir, '..');
const repositoryRoot = path.resolve(packageRoot, '../..');
const changesetRoot = path.join(repositoryRoot, '.changeset');
const precompilesRoot = path.join(repositoryRoot, 'packages/precompiles');
const cliPath = path.join(packageRoot, 'dist/main.js');
const templateManifestPath = path.join(packageRoot, 'templates/next-template/package.json');
Expand Down Expand Up @@ -88,6 +90,26 @@ async function pathExists(target: string): Promise<boolean> {
.catch(() => false);
}

async function computePendingReleasePlan(tempRoot: string): Promise<ReleasePlan> {
// A Version Packages branch consumes every changeset while it writes the bumped manifests
// this smoke test exists to validate, and `changeset status` rejects that state because the
// branch changes packages without a changeset. Only require a status report while changesets
// are still waiting to be released.
const changesetsPending = hasPendingChangesets(await fs.readdir(changesetRoot));
const releasePlanPath = path.join(tempRoot, 'changeset-status.json');
const exitCode = await run(
'Compute pending release metadata',
[process.execPath, 'run', 'changeset', 'status', '--output', releasePlanPath],
repositoryRoot,
changesetsPending
);
if (exitCode !== 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[suggestion] Once .changeset is consumed, this treats every nonzero exit as the expected Version Packages condition. A malformed config.json, an unknown package reference, a missing changeset binary, or any runtime crash all produce the same message and an invented { releases: [] } — which then routes selectPrecompilesSource to the current-manifest basis and lets the smoke pass. Since this is exactly the branch state the fix exists to make green, a genuine regression here would stay invisible.

Consider narrowing the suppression to the specific failure. capture() is already available above, so you could match the known lint error rather than the exit code:

const { exitCode, stderr } = await capture([...], repositoryRoot);
if (exitCode !== 0) {
	if (changesetsPending || !/no changesets were found/i.test(stderr)) {
		throw new Error(`Compute pending release metadata failed with exit code ${exitCode}`);
	}
	console.log('Every changeset has already been consumed, so the error above is expected and no release is pending.');
	return { releases: [] };
}

That keeps fatal semantics honest while still tolerating the one state you intend to tolerate. (Also raised by Codex as P2.)

console.log('Every changeset has already been consumed, so the error above is expected and no release is pending.');
return { releases: [] };
}
return JSON.parse(await fs.readFile(releasePlanPath, 'utf8')) as ReleasePlan;
}

async function resolvePrecompilesTarget(tempRoot: string): Promise<PrecompilesTarget> {
const templateManifest = JSON.parse(await fs.readFile(templateManifestPath, 'utf8')) as {
dependencies?: Record<string, string>;
Expand All @@ -97,9 +119,7 @@ async function resolvePrecompilesTarget(tempRoot: string): Promise<PrecompilesTa
throw new Error('The template must pin @sei-js/precompiles to one exact version.');
}

const releasePlanPath = path.join(tempRoot, 'changeset-status.json');
await run('Compute pending release metadata', [process.execPath, 'run', 'changeset', 'status', '--output', releasePlanPath], repositoryRoot);
const releasePlan = JSON.parse(await fs.readFile(releasePlanPath, 'utf8')) as ReleasePlan;
const releasePlan = await computePendingReleasePlan(tempRoot);
const pendingRelease = releasePlan.releases.find((release) => release.name === '@sei-js/precompiles');
const currentManifest = JSON.parse(await fs.readFile(path.join(precompilesRoot, 'package.json'), 'utf8')) as {
version: string;
Expand Down
11 changes: 11 additions & 0 deletions packages/create-sei/src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { promises as fs } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { BRAND_ASSET_HASHES } from '../brand-assets';
import { hasPendingChangesets } from '../scripts/pending-changesets';
import { selectPrecompilesSource } from '../scripts/select-precompiles-source';
import { SEI_NEUTRAL_RAMP } from '../templates/next-template/src/theme';

Expand Down Expand Up @@ -83,6 +84,16 @@ describe('precompiles source selection', () => {
});
});

describe('pending changeset detection', () => {
test('counts changeset markdown as pending', () => {
expect(hasPendingChangesets(['README.md', 'config.json', 'fix-create-sei-scaffold.md'])).toBe(true);
});

test('treats a versioned changeset folder as consumed', () => {
expect(hasPendingChangesets(['README.md', 'config.json'])).toBe(false);
});
});

describe('CLI', () => {
beforeAll(async () => {
const { stdout, stderr, exitCode } = await runProcess([process.execPath, 'run', 'build'], packageRoot);
Expand Down
Loading