diff --git a/README.md b/README.md index 8c60dd3..6a64a69 100644 --- a/README.md +++ b/README.md @@ -39,14 +39,20 @@ npx canton-dev-tools verify-dars npx canton-dev-tools backup-dar --package WrappedAssets-v01 --version 0.0.1 npx canton-dev-tools check-dar-version-policy --all npx canton-dev-tools check-upgrade-compat -npx canton-dev-tools sync-splice-dars --config splice-dars.json +npx canton-dev-tools sync-splice-dars ``` `backup-dar` / version-policy / upgrade-compat skip `Test` packages by default. Pass `--package` with the daml.yaml name, source dir, or a fuzzy alias (e.g. `wrappedAssets`). ### `sync-splice-dars` config -Create `splice-dars.json` in the repo root (or pass `--config` / `CANTON_SPLICE_DARS_CONFIG`): +By default, sync uses the packaged pin at `config/default-splice-dars.json` (MainNet Splice +**0.6.14** / commit `398919a5b13479877fd61587003ba7a4ba00091b`, token-standard + amulet DARs). +Optional overrides, in order: + +1. `--config ` +2. `CANTON_SPLICE_DARS_CONFIG` +3. repo-root `splice-dars.json` (per-repo pin when it must differ from the shared default) ```json { diff --git a/bin/canton-dev-tools b/bin/canton-dev-tools index 0c66bad..19578d8 100755 --- a/bin/canton-dev-tools +++ b/bin/canton-dev-tools @@ -64,7 +64,7 @@ Environment: CANTON_LOCALNET_AUTH_MODE oauth2 (package default) or shared-secret CANTON_LOCALNET_INFRA_ONLY Defaults to true for this package binary SPLICE_REF / SPLICE_REPO Overrides for sync-splice-dars - CANTON_SPLICE_DARS_CONFIG Path to splice-dars.json + CANTON_SPLICE_DARS_CONFIG Optional path override (else packaged default / splice-dars.json) USAGE } diff --git a/config/default-splice-dars.json b/config/default-splice-dars.json new file mode 100644 index 0000000..b962396 --- /dev/null +++ b/config/default-splice-dars.json @@ -0,0 +1,70 @@ +{ + "spliceRef": "398919a5b13479877fd61587003ba7a4ba00091b", + "requiredDars": [ + { + "file": "splice-amulet-0.1.16.dar", + "sha256": "d799e1750bce6099daa996049a318e78f8b424e4e091268012fc3dc111b7a4cb" + }, + { + "file": "splice-amulet-0.1.19.dar", + "sha256": "003554a4f3e6a6ee56f5edd55f0be3d521c4b075fc0c3efdc45e114561ad390d" + }, + { + "file": "splice-api-featured-app-v1-1.0.0.dar", + "sha256": "01df2e1b86e3a1b9ab55e97e42f280563c9d1fbe909963c74bef62dd642aa3ee" + }, + { + "file": "splice-api-token-allocation-instruction-v1-1.0.0.dar", + "sha256": "e2607ca3a1d735a82d3066b78132aa8f94b1886c99a5f14148742d252c7220a2" + }, + { + "file": "splice-api-token-allocation-instruction-v2-1.0.0.dar", + "sha256": "31de6be30385936105b24c8ea544bb25f6eede0d0c008154a11462bb6d14a17b" + }, + { + "file": "splice-api-token-allocation-request-v2-1.0.0.dar", + "sha256": "aea00bf92c841807c7b02a82eaf2a1599ec8a0f11b2de05edd31fc140a2426b9" + }, + { + "file": "splice-api-token-allocation-v1-1.0.0.dar", + "sha256": "c3f3b447142577ea4fa7d912ca11cd6821de7588e324e8877425932a02fccaa1" + }, + { + "file": "splice-api-token-allocation-v2-1.0.0.dar", + "sha256": "e349d3a1952cdd52ed55333bef35e9ba40e6e0379521d65556c6aa2f50d9a1fd" + }, + { + "file": "splice-api-token-burn-mint-v1-1.0.0.dar", + "sha256": "a18e85c4841a278bce000df8329c3f0e2fee3b30b55dd6a31492d10a72b4f9c1" + }, + { + "file": "splice-api-token-holding-v1-1.0.0.dar", + "sha256": "ef75f8eb41a65810221784fdb78bb9dfac7cb22245aba14fa7cb7f69c34e0175" + }, + { + "file": "splice-api-token-holding-v2-1.0.0.dar", + "sha256": "f044b34d7a28c2b6c8a540af720297d55450a889b451dc4eed64a9914f1d6b00" + }, + { + "file": "splice-api-token-metadata-v1-1.0.0.dar", + "sha256": "455eb160cb5abd4ae9918a6fbb9dad471f721adda39f0e5c76feef08d05637fc" + }, + { + "file": "splice-api-token-transfer-events-v2-1.0.0.dar", + "sha256": "9a1ebe5aece0a7f1a49069fd7aba0ced92c4ae94e5e3d49cdb097733a87dddbc" + }, + { + "file": "splice-api-token-transfer-instruction-v1-1.0.0.dar", + "sha256": "e4c73aa7ae73fb2fc330b938ffb99f568792321640ba4b9472902aa8d742c994" + }, + { + "file": "splice-api-token-transfer-instruction-v2-1.0.0.dar", + "sha256": "720e766456b33abdd26bfc21e54d19413559f0a7ee52000e12d3b6c308199faa" + }, + { + "file": "splice-token-standard-utils-2.0.0.dar", + "sha256": "60cd68514b48efacea343722c3692436f300d2e33f98d9e5bc0673347961aca0" + } + ], + "syncAdminProtos": true +} diff --git a/package-lock.json b/package-lock.json index 1465748..5c42921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@fairmint/canton-dev-tools", - "version": "0.1.0", + "version": "0.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@fairmint/canton-dev-tools", - "version": "0.1.0", + "version": "0.1.4", "license": "MIT", "dependencies": { - "yaml": "^2.9.0" + "yaml": "2.9.0" }, "bin": { "canton-dev-tools": "bin/canton-dev-tools" diff --git a/package.json b/package.json index 246710e..08edba5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fairmint/canton-dev-tools", - "version": "0.1.0", + "version": "0.1.4", "description": "Shared LocalNet CLI, DAML package tooling, and Canton integration-test helpers for Fairmint", "keywords": [ "canton", @@ -46,6 +46,7 @@ }, "files": [ "bin/canton-dev-tools", + "config/default-splice-dars.json", "scripts/localnet-cloud.sh", "scripts/install-dpm-sdks.sh", "dist/**" diff --git a/src/cli.ts b/src/cli.ts index e14692c..928d50c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -23,7 +23,7 @@ DAML package commands (run from a multi-package repo root): backup-dar Backup a built DAR into dars/ check-dar-version-policy Enforce DAR version / deployment tag policy check-upgrade-compat Run dpm upgrade-check against backups - sync-splice-dars Fetch pinned Splice DARs from splice-dars.json + sync-splice-dars Fetch pinned Splice DARs (packaged default or splice-dars.json) install-dpm-sdks Install Daml SDKs from daml.yaml (shell helper) Common options: diff --git a/src/daml/check-dar-version-policy.ts b/src/daml/check-dar-version-policy.ts index 7178e34..a5c8cbb 100644 --- a/src/daml/check-dar-version-policy.ts +++ b/src/daml/check-dar-version-policy.ts @@ -90,9 +90,13 @@ function loadLockAtRef(rootDir: string, ref: string): DarsLock { return { version: 1, packages: {} }; } - const parsed = parseDarsLockContent(gitText(rootDir, ['show', `${ref}:dars/dars.lock`]), `${ref}:dars/dars.lock`, { - requireVersion1: true, - }); + const parsed = parseDarsLockContent( + gitText(rootDir, ['show', `${ref}:dars/dars.lock`]), + `${ref}:dars/dars.lock`, + { + requireVersion1: true, + } + ); return parsed; } diff --git a/src/daml/dar-utils.ts b/src/daml/dar-utils.ts index 72f39ce..dbbf55a 100644 --- a/src/daml/dar-utils.ts +++ b/src/daml/dar-utils.ts @@ -167,13 +167,7 @@ export function getDarLockKey(packageName: string, version: string, darName: str ['version', version], ['darName', darName], ] as const) { - if ( - !value || - value.includes('/') || - value.includes('\\') || - value === '.' || - value === '..' - ) { + if (!value || value.includes('/') || value.includes('\\') || value === '.' || value === '..') { throw new Error(`Unsafe ${label} for DAR lock key: ${value}`); } } diff --git a/src/daml/prepare-build.ts b/src/daml/prepare-build.ts index 008c160..ef1aaf9 100644 --- a/src/daml/prepare-build.ts +++ b/src/daml/prepare-build.ts @@ -8,7 +8,11 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import * as yaml from 'yaml'; -import { assertSafeRelativePath, normalizeRelativePath, resolveContainedPath } from './sync-splice-dars'; +import { + assertSafeRelativePath, + normalizeRelativePath, + resolveContainedPath, +} from './sync-splice-dars'; export interface PrepareBuildOptions { rootDir: string; @@ -148,7 +152,11 @@ export function prepareBuild(options: PrepareBuildOptions): string[] { const generatedPackages: string[] = []; for (const [sourceDir, packageName] of packageNames) { - const sourcePath = resolveContainedPath(rootDir, sourceDir, 'multi-package.yaml packages entry'); + const sourcePath = resolveContainedPath( + rootDir, + sourceDir, + 'multi-package.yaml packages entry' + ); const targetPath = resolveContainedPath(buildRoot, packageName, 'daml.yaml name'); assertPathInsideRoot(rootDir, targetPath, 'prepare-build target'); copyDir(sourcePath, targetPath); diff --git a/src/daml/sync-splice-dars.ts b/src/daml/sync-splice-dars.ts index 9e44e8b..285d2ad 100644 --- a/src/daml/sync-splice-dars.ts +++ b/src/daml/sync-splice-dars.ts @@ -1,8 +1,13 @@ /** * Sync pinned Splice DARs (and optionally Canton admin protos) into a consumer repo. * - * Requires a JSON config (default `splice-dars.json` in the repo root) so each consumer - * owns SPLICE_REF + REQUIRED_DARS. Env overrides: SPLICE_REPO, SPLICE_REF, CANTON_SPLICE_DARS_CONFIG. + * Config resolution order: + * 1. `--config` / `options.configPath` + * 2. `CANTON_SPLICE_DARS_CONFIG` + * 3. consumer `splice-dars.json` (optional per-repo override) + * 4. packaged default at `config/default-splice-dars.json` (shared Fairmint pin) + * + * Env overrides: SPLICE_REPO, SPLICE_REF, CANTON_SPLICE_DARS_CONFIG. */ import { execFileSync } from 'node:child_process'; @@ -60,19 +65,32 @@ const DEFAULT_DARS_RELATIVE_DIR = 'libs/splice/daml/dars'; const DEFAULT_ADMIN_PROTO_RELATIVE_DIR = 'libs/splice/canton/community/admin-api/src/main/protobuf'; const SYNC_STATE_FILENAME = '.canton-splice-sync-state.json'; -function resolveConfigPath(rootDir: string, configPath?: string): string { +/** Packaged shared pin shipped with `@fairmint/canton-dev-tools`. */ +export function getDefaultSpliceDarsConfigPath(): string { + return path.join(__dirname, '..', '..', 'config', 'default-splice-dars.json'); +} + +/** + * Resolve which splice-dars config file to load. + * Consumer `splice-dars.json` overrides the packaged default when present. + */ +export function resolveSyncSpliceDarsConfigPath(rootDir: string, configPath?: string): string { if (configPath) return path.resolve(configPath); if (process.env['CANTON_SPLICE_DARS_CONFIG']) { return path.resolve(process.env['CANTON_SPLICE_DARS_CONFIG']); } - return path.join(rootDir, 'splice-dars.json'); + const consumerConfig = path.join(rootDir, 'splice-dars.json'); + if (fs.existsSync(consumerConfig)) { + return consumerConfig; + } + return getDefaultSpliceDarsConfigPath(); } export function loadSyncSpliceDarsConfig(configPath: string): SyncSpliceDarsConfig { if (!fs.existsSync(configPath)) { throw new Error( `Splice DAR config not found: ${configPath}\n` + - 'Create splice-dars.json (or pass --config) with spliceRef + requiredDars.\n' + + 'Expected packaged config/default-splice-dars.json, or create splice-dars.json / pass --config.\n' + 'See README for the expected shape.' ); } @@ -440,8 +458,7 @@ function syncSpliceDarsFiles(rootDir: string, config: SyncSpliceDarsConfig): voi DEFAULT_ADMIN_PROTO_RELATIVE_DIR, 'adminProtoRelativeDir' ); - const adminProtoSourceRelative = - config.adminProtoSourceDir || DEFAULT_ADMIN_PROTO_SOURCE_DIR; + const adminProtoSourceRelative = config.adminProtoSourceDir || DEFAULT_ADMIN_PROTO_SOURCE_DIR; const packageService = config.adminProtoPackageService || DEFAULT_ADMIN_PROTO_PACKAGE_SERVICE; const syncAdminProtos = config.syncAdminProtos !== false; if (syncAdminProtos) { @@ -464,9 +481,7 @@ function syncSpliceDarsFiles(rootDir: string, config: SyncSpliceDarsConfig): voi console.log(`Fetching Splice DARs from ${spliceRepo} at ${spliceRef}`); runGit(['clone', '--filter=blob:none', '--sparse', '--no-checkout', spliceRepo, cloneDir]); runGit(['checkout', spliceRef], cloneDir); - const sparsePaths = syncAdminProtos - ? ['daml/dars', adminProtoSourceRelative] - : ['daml/dars']; + const sparsePaths = syncAdminProtos ? ['daml/dars', adminProtoSourceRelative] : ['daml/dars']; runGit(['sparse-checkout', 'set', ...sparsePaths], cloneDir); fs.mkdirSync(darsDir, { recursive: true }); @@ -537,7 +552,8 @@ export function syncSpliceDars(options: SyncSpliceDarsOptions): void { const rootDir = path.resolve(options.rootDir); const force = options.force ?? false; const config = - options.config ?? loadSyncSpliceDarsConfig(resolveConfigPath(rootDir, options.configPath)); + options.config ?? + loadSyncSpliceDarsConfig(resolveSyncSpliceDarsConfigPath(rootDir, options.configPath)); for (const dar of config.requiredDars) { assertSafeRelativePath(dar.file, 'requiredDars.file'); diff --git a/src/daml/verify-dars.ts b/src/daml/verify-dars.ts index 33532cb..7447b2e 100644 --- a/src/daml/verify-dars.ts +++ b/src/daml/verify-dars.ts @@ -194,7 +194,9 @@ export function runVerifyDarsCli(options: VerifyDarsOptions): void { ); console.error(' 2. If changes were accidental, restore the original DAR files'); console.error(' 3. Never modify backed-up DAR files directly'); - console.error(' 4. Run with --update to fix dars.lock hash/size/untracked (use with caution)\n'); + console.error( + ' 4. Run with --update to fix dars.lock hash/size/untracked (use with caution)\n' + ); } else if (result.missing > 0) { console.error( 'Tracked DAR files are missing from disk. Restore them before re-running --update.\n' diff --git a/src/testing/testConfig.ts b/src/testing/testConfig.ts index ba94b49..5cc0fb7 100644 --- a/src/testing/testConfig.ts +++ b/src/testing/testConfig.ts @@ -94,10 +94,7 @@ export function buildLocalnetClientConfig(): ClientConfig { /** @deprecated Prefer {@link buildLocalnetClientConfig}. */ export const buildIntegrationTestClientConfig = buildLocalnetClientConfig; -function buildAuthConfig( - apiType: string, - options: { preferOAuth?: boolean } = {} -): AuthConfig { +function buildAuthConfig(apiType: string, options: { preferOAuth?: boolean } = {}): AuthConfig { const apiPrefix = `FAIRMINT_TEST_${apiType}`; const authUrl = getEnv('FAIRMINT_TEST_AUTH_URL'); const clientId = diff --git a/test/unit/cip56/transferSmokeTraffic.test.ts b/test/unit/cip56/transferSmokeTraffic.test.ts index 2eda605..d9064f2 100644 --- a/test/unit/cip56/transferSmokeTraffic.test.ts +++ b/test/unit/cip56/transferSmokeTraffic.test.ts @@ -5,7 +5,9 @@ import { describe('CIP-56 transfer smoke traffic error classification', (): void => { it('detects HTTP 429 and traffic-balance messages in nested causes', (): void => { - const cause = new Error('ApiError: HTTP 429 [request: post http://localhost:3903/api/validator/v0/admin/users]'); + const cause = new Error( + 'ApiError: HTTP 429 [request: post http://localhost:3903/api/validator/v0/admin/users]' + ); const wrapped = new Error( 'UnknownMutationOutcomeError: The outcome of POST http://localhost:3903/api/validator/v0/admin/users is unknown after 1 request attempt', { cause } @@ -13,7 +15,9 @@ describe('CIP-56 transfer smoke traffic error classification', (): void => { expect(isTransientValidatorTrafficError(wrapped)).toBe(true); expect(collectErrorMessages(wrapped)).toContain('HTTP 429'); - const traffic = new Error('ABORTED: Traffic balance below reserved traffic amount (0 < 200000)'); + const traffic = new Error( + 'ABORTED: Traffic balance below reserved traffic amount (0 < 200000)' + ); expect(isTransientValidatorTrafficError(traffic)).toBe(true); }); diff --git a/test/unit/daml/packages.test.ts b/test/unit/daml/packages.test.ts index 4583d16..bd94cb8 100644 --- a/test/unit/daml/packages.test.ts +++ b/test/unit/daml/packages.test.ts @@ -161,16 +161,10 @@ describe('daml package discovery + prepare-build', (): void => { }); it('rejects multi-package source dirs and daml.yaml names that escape the build tree', (): void => { - writeFileSync( - join(rootDir, 'multi-package.yaml'), - `packages:\n - ../../../.github\n` - ); + writeFileSync(join(rootDir, 'multi-package.yaml'), `packages:\n - ../../../.github\n`); expect(() => prepareBuild({ rootDir })).toThrow(/Unsafe/); - writeFileSync( - join(rootDir, 'multi-package.yaml'), - `packages:\n - WrappedAssets-v01\n` - ); + writeFileSync(join(rootDir, 'multi-package.yaml'), `packages:\n - WrappedAssets-v01\n`); writeFileSync( join(rootDir, 'WrappedAssets-v01', 'daml.yaml'), `sdk-version: 3.5.2\nname: ../../../.github\nsource: daml\nversion: 0.0.1\ndependencies: []\n` @@ -179,16 +173,10 @@ describe('daml package discovery + prepare-build', (): void => { }); it('rejects escaping source dirs and daml.yaml names during package discovery', (): void => { - writeFileSync( - join(rootDir, 'multi-package.yaml'), - `packages:\n - ../outside\n` - ); + writeFileSync(join(rootDir, 'multi-package.yaml'), `packages:\n - ../outside\n`); expect(() => discoverPackages(rootDir)).toThrow(/Unsafe/); - writeFileSync( - join(rootDir, 'multi-package.yaml'), - `packages:\n - WrappedAssets-v01\n` - ); + writeFileSync(join(rootDir, 'multi-package.yaml'), `packages:\n - WrappedAssets-v01\n`); writeFileSync( join(rootDir, 'WrappedAssets-v01', 'daml.yaml'), `sdk-version: 3.5.2\nname: ../escape\nsource: daml\nversion: 0.0.1\ndependencies: []\n` diff --git a/test/unit/daml/upgrade-and-policy.test.ts b/test/unit/daml/upgrade-and-policy.test.ts index 7c9cb6e..01bac26 100644 --- a/test/unit/daml/upgrade-and-policy.test.ts +++ b/test/unit/daml/upgrade-and-policy.test.ts @@ -3,11 +3,7 @@ import { execFileSync } from 'node:child_process'; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { - checkDarVersionPolicy, - checkUpgradeCompatibility, - saveDarsLock, -} from '../../../src/daml'; +import { checkDarVersionPolicy, checkUpgradeCompatibility, saveDarsLock } from '../../../src/daml'; function sha256(content: string | Buffer): string { return createHash('sha256').update(content).digest('hex'); @@ -99,14 +95,7 @@ describe('checkUpgradeCompatibility', (): void => { it('rejects non-semver versions in dars.lock when selecting upgrade baselines', (): void => { const currentBytes = Buffer.from('current-dar'); - const distDir = join( - rootDir, - 'generated', - 'build', - 'WrappedAssets-v01', - '.daml', - 'dist' - ); + const distDir = join(rootDir, 'generated', 'build', 'WrappedAssets-v01', '.daml', 'dist'); mkdirSync(distDir, { recursive: true }); writeFileSync(join(distDir, 'WrappedAssets-v01-0.0.2.dar'), currentBytes); mkdirSync(join(rootDir, 'dars', 'WrappedAssets-v01', '0.0.2'), { recursive: true }); @@ -134,21 +123,12 @@ describe('checkUpgradeCompatibility', (): void => { }, }); - expect(() => checkUpgradeCompatibility({ rootDir })).toThrow( - /Invalid semver in dars.lock key/ - ); + expect(() => checkUpgradeCompatibility({ rootDir })).toThrow(/Invalid semver in dars.lock key/); }); it('fails when an older lock baseline is missing on disk instead of treating as first release', (): void => { const currentBytes = Buffer.from('current-dar'); - const distDir = join( - rootDir, - 'generated', - 'build', - 'WrappedAssets-v01', - '.daml', - 'dist' - ); + const distDir = join(rootDir, 'generated', 'build', 'WrappedAssets-v01', '.daml', 'dist'); mkdirSync(distDir, { recursive: true }); writeFileSync(join(distDir, 'WrappedAssets-v01-0.0.2.dar'), currentBytes); @@ -199,14 +179,7 @@ describe('checkUpgradeCompatibility', (): void => { it('fails when the committed current backup exists but does not match dars.lock', (): void => { const buildBytes = Buffer.from('build-dar'); const corruptBackup = Buffer.from('corrupt-backup-bytes'); - const distDir = join( - rootDir, - 'generated', - 'build', - 'WrappedAssets-v01', - '.daml', - 'dist' - ); + const distDir = join(rootDir, 'generated', 'build', 'WrappedAssets-v01', '.daml', 'dist'); mkdirSync(distDir, { recursive: true }); writeFileSync(join(distDir, 'WrappedAssets-v01-0.0.2.dar'), buildBytes); mkdirSync(join(rootDir, 'dars', 'WrappedAssets-v01', '0.0.2'), { recursive: true }); diff --git a/test/unit/daml/verify-and-safety.test.ts b/test/unit/daml/verify-and-safety.test.ts index 7690c66..270d4fc 100644 --- a/test/unit/daml/verify-and-safety.test.ts +++ b/test/unit/daml/verify-and-safety.test.ts @@ -15,10 +15,12 @@ import { assertGitCommitRef, assertSafeRelativePath, DarIntegrityError, + getDefaultSpliceDarsConfigPath, hashDirectoryTree, loadSyncSpliceDarsConfig, requireBackedUpDar, resolveContainedPath, + resolveSyncSpliceDarsConfigPath, saveDarsLock, syncSpliceDars, verifyDars, @@ -127,7 +129,9 @@ describe('path containment helpers', (): void => { ); expect(() => assertSafeRelativePath('.', 'darsRelativeDir')).toThrow(/Unsafe/); expect(() => assertSafeRelativePath('..', 'adminProtoRelativeDir')).toThrow(/Unsafe/); - expect(() => assertSafeRelativePath('./main', 'multi-package.yaml packages entry')).not.toThrow(); + expect(() => + assertSafeRelativePath('./main', 'multi-package.yaml packages entry') + ).not.toThrow(); expect(() => assertSafeRelativePath('ok/file.dar', 'requiredDars.file')).not.toThrow(); }); @@ -158,6 +162,51 @@ describe('path containment helpers', (): void => { }); describe('sync-splice-dars path safety', (): void => { + it('falls back to the packaged default when the consumer has no splice-dars.json', (): void => { + const rootDir = mkdtempSync(join(tmpdir(), 'canton-dev-tools-sync-default-')); + const previous = process.env['CANTON_SPLICE_DARS_CONFIG']; + try { + delete process.env['CANTON_SPLICE_DARS_CONFIG']; + const resolved = resolveSyncSpliceDarsConfigPath(rootDir); + expect(resolved).toBe(getDefaultSpliceDarsConfigPath()); + expect(existsSync(resolved)).toBe(true); + const config = loadSyncSpliceDarsConfig(resolved); + expect(config.spliceRef).toMatch(/^[0-9a-f]{40}$/); + expect(config.requiredDars.length).toBeGreaterThan(0); + } finally { + if (previous === undefined) { + delete process.env['CANTON_SPLICE_DARS_CONFIG']; + } else { + process.env['CANTON_SPLICE_DARS_CONFIG'] = previous; + } + rmSync(rootDir, { recursive: true, force: true }); + } + }); + + it('prefers consumer splice-dars.json over the packaged default', (): void => { + const rootDir = mkdtempSync(join(tmpdir(), 'canton-dev-tools-sync-override-')); + const previous = process.env['CANTON_SPLICE_DARS_CONFIG']; + try { + delete process.env['CANTON_SPLICE_DARS_CONFIG']; + const consumerConfig = join(rootDir, 'splice-dars.json'); + writeFileSync( + consumerConfig, + JSON.stringify({ + spliceRef: 'deadbeefdeadbeefdeadbeefdeadbeefdeadbeef', + requiredDars: [{ file: 'a.dar', sha256: 'abc' }], + }) + ); + expect(resolveSyncSpliceDarsConfigPath(rootDir)).toBe(consumerConfig); + } finally { + if (previous === undefined) { + delete process.env['CANTON_SPLICE_DARS_CONFIG']; + } else { + process.env['CANTON_SPLICE_DARS_CONFIG'] = previous; + } + rmSync(rootDir, { recursive: true, force: true }); + } + }); + it('rejects unsafe directory fields in config JSON', (): void => { const rootDir = mkdtempSync(join(tmpdir(), 'canton-dev-tools-sync-cfg-')); try { diff --git a/test/unit/scripts/package-artifacts.test.ts b/test/unit/scripts/package-artifacts.test.ts index 7ab3c63..f55e369 100644 --- a/test/unit/scripts/package-artifacts.test.ts +++ b/test/unit/scripts/package-artifacts.test.ts @@ -35,6 +35,7 @@ describe('package artifact policy', (): void => { expect(pkg.files).toEqual([ 'bin/canton-dev-tools', + 'config/default-splice-dars.json', 'scripts/localnet-cloud.sh', 'scripts/install-dpm-sdks.sh', 'dist/**',