Skip to content

Commit cdf5c89

Browse files
authored
fix(export): add polkadot adapter to version registry and harden versioning pipeline (#353)
* fix(export): add polkadot adapter to version registry and harden versioning pipeline Polkadot adapter was missing from versions.ts, update-export-versions script, and CLI local overrides, causing production exports to emit workspace:* which fails for end users. Staging worked by accident because its fallback always outputs the 'rc' dist-tag. Adds a defensive fallback in the production code path so unregistered adapters resolve to 'latest' instead of leaking workspace:*. Introduces VersioningSafetyGuard tests covering all four developed ecosystems, and updates CI e2e matrix from [evm, solana, stellar] to [evm, stellar, polkadot, midnight]. * fix(export): address PR review comments Fix EXPORT_TEST_CHAIN → EXPORT_TEST_ECOSYSTEM env var mismatch in export-app.cjs so the CI e2e matrix actually tests each chain instead of always falling back to evm. Update CLI help text to list the current supported ecosystems. Add a unit test verifying the workspace:* → latest production fallback. * ci(export): limit e2e matrix to vitest-compatible ecosystems Stellar and Midnight adapters have third-party dependencies with ESM/CJS incompatibilities that prevent them from loading in the Vitest runner. Restrict the CI e2e matrix to EVM and Polkadot. Versioning correctness for all ecosystems is covered by VersioningSafetyGuard.test.ts.
1 parent 2e23c29 commit cdf5c89

12 files changed

Lines changed: 811 additions & 446 deletions

.github/workflows/export-testing.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ jobs:
105105
strategy:
106106
fail-fast: false
107107
matrix:
108-
chain: [evm, solana, stellar]
108+
# Stellar (@stellar/freighter-api CJS) and Midnight (@apollo/client ESM directory imports)
109+
# have adapter deps incompatible with the Vitest ESM loader. Versioning correctness for
110+
# all ecosystems is validated by VersioningSafetyGuard.test.ts (mock-based, no dynamic import).
111+
chain: [evm, polkadot]
109112
node-version: [22.x]
110113

111114
steps:

apps/builder/src/export/PackageManager.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,14 @@ export class PackageManager {
532532
}
533533
} else {
534534
// Production: Use stable published versions
535-
if (managedVersion.startsWith('^') || managedVersion === 'workspace:*') {
535+
if (managedVersion === 'workspace:*') {
536+
logger.warn(
537+
'PackageManager',
538+
`Adapter package "${pkgName}" has no managed version in versions.ts. ` +
539+
`Falling back to 'latest' dist-tag for production.`
540+
);
541+
updatedDependencies[pkgName] = 'latest';
542+
} else if (managedVersion.startsWith('^')) {
536543
updatedDependencies[pkgName] = managedVersion;
537544
} else {
538545
updatedDependencies[pkgName] = `^${managedVersion}`;
Lines changed: 40 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,24 @@
11
import { beforeEach, describe, expect, it } from 'vitest';
22

3-
import type { EvmNetworkConfig, NetworkConfig, SolanaNetworkConfig } from '@openzeppelin/ui-types';
3+
import type { Ecosystem, NetworkConfig } from '@openzeppelin/ui-types';
44

5+
import { adapterPackageMap, getNetworksByEcosystem } from '../../core/ecosystemManager';
56
import { AppExportSystem } from '../AppExportSystem';
67
import { createMinimalContractSchema, createMinimalFormConfig } from '../utils/testConfig';
78
import { extractFilesFromZip } from '../utils/zipInspector';
89

9-
// Define mock network configs
10-
const mockEvmNetworkConfig: EvmNetworkConfig = {
11-
id: 'test-export-adapter-evm',
12-
name: 'Test Export EVM',
13-
exportConstName: 'mockEvmNetworkConfig',
14-
ecosystem: 'evm',
15-
network: 'ethereum',
16-
type: 'testnet',
17-
isTestnet: true,
18-
chainId: 1337,
19-
rpcUrl: 'http://localhost:8545',
20-
nativeCurrency: { name: 'TETH', symbol: 'TETH', decimals: 18 },
21-
apiUrl: '',
22-
};
10+
// Stellar and Midnight adapters have ESM/CJS compatibility issues when loaded
11+
// dynamically in vitest. Full e2e export tests are limited to EVM and Polkadot.
12+
// Versioning correctness for ALL ecosystems is verified in VersioningSafetyGuard.test.ts.
13+
const EXPORTABLE_ECOSYSTEMS: Ecosystem[] = ['evm', 'polkadot'];
2314

24-
const mockSolanaNetworkConfig: SolanaNetworkConfig = {
25-
id: 'test-export-adapter-solana',
26-
name: 'Test Export Solana',
27-
exportConstName: 'mockSolanaNetworkConfig',
28-
ecosystem: 'solana',
29-
network: 'solana',
30-
type: 'testnet',
31-
isTestnet: true,
32-
rpcEndpoint: 'mock',
33-
commitment: 'confirmed',
34-
};
15+
async function getFirstNetwork(ecosystem: Ecosystem): Promise<NetworkConfig> {
16+
const networks = await getNetworksByEcosystem(ecosystem);
17+
if (networks.length === 0) {
18+
throw new Error(`No networks found for ecosystem: ${ecosystem}`);
19+
}
20+
return networks[0];
21+
}
3522

3623
describe('Adapter Integration Tests', () => {
3724
let exportSystem: AppExportSystem;
@@ -40,7 +27,6 @@ describe('Adapter Integration Tests', () => {
4027
exportSystem = new AppExportSystem();
4128
});
4229

43-
// Helper function to get exported files and parsed package.json
4430
async function getExportedPackageJson(
4531
networkConfig: NetworkConfig,
4632
functionName: string = 'transfer'
@@ -64,28 +50,36 @@ describe('Adapter Integration Tests', () => {
6450
}
6551

6652
describe('Package.json Adapter Dependencies', () => {
67-
it('should include correct dependencies for EVM in package.json', async () => {
68-
const { packageJson } = await getExportedPackageJson(mockEvmNetworkConfig);
69-
70-
// Check required packages are present
71-
expect(packageJson.dependencies).toHaveProperty('@openzeppelin/ui-types');
72-
expect(packageJson.dependencies).toHaveProperty('@openzeppelin/ui-builder-adapter-evm');
73-
74-
// Optional: Check if specific SDKs (like ethers) are NOT directly listed if they are peer/sub-dependencies
75-
// expect(packageJson.dependencies).not.toHaveProperty('ethers');
76-
});
53+
it.each(EXPORTABLE_ECOSYSTEMS)(
54+
'should include correct adapter dependency for %s in package.json',
55+
async (ecosystem) => {
56+
const networkConfig = await getFirstNetwork(ecosystem);
57+
const { packageJson } = await getExportedPackageJson(networkConfig);
58+
const expectedAdapter = adapterPackageMap[ecosystem];
7759

78-
it('should include correct dependencies for Solana in package.json', async () => {
79-
const { packageJson } = await getExportedPackageJson(mockSolanaNetworkConfig);
60+
expect(packageJson.dependencies).toHaveProperty('@openzeppelin/ui-types');
61+
expect(packageJson.dependencies).toHaveProperty(expectedAdapter);
62+
}
63+
);
8064

81-
// Check required packages are present
82-
expect(packageJson.dependencies).toHaveProperty('@openzeppelin/ui-types');
83-
expect(packageJson.dependencies).toHaveProperty('@openzeppelin/ui-builder-adapter-solana');
65+
it.each(EXPORTABLE_ECOSYSTEMS)(
66+
'should not include adapters from other ecosystems in %s export',
67+
async (ecosystem) => {
68+
const networkConfig = await getFirstNetwork(ecosystem);
69+
const { packageJson } = await getExportedPackageJson(networkConfig);
70+
const expectedAdapter = adapterPackageMap[ecosystem];
8471

85-
// Optional: Check specific SDKs are NOT directly listed
86-
// expect(packageJson.dependencies).not.toHaveProperty('@solana/web3.js');
87-
});
72+
for (const [otherEcosystem, otherAdapter] of Object.entries(adapterPackageMap)) {
73+
if (otherEcosystem !== ecosystem) {
74+
expect(
75+
packageJson.dependencies,
76+
`${ecosystem} export should not contain adapter for ${otherEcosystem}`
77+
).not.toHaveProperty(otherAdapter);
78+
}
79+
}
8880

89-
// Add similar tests for Stellar and Midnight if needed
81+
expect(packageJson.dependencies).toHaveProperty(expectedAdapter);
82+
}
83+
);
9084
});
9185
});

0 commit comments

Comments
 (0)