From b3984cd79a4619bdf41d4a1ba2bcfa61416f29bd Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 11 Aug 2026 18:52:53 +0000 Subject: [PATCH 1/2] Ship shared default splice-dars.json for sync-splice-dars Consumers can drop per-repo splice-dars.json and use the packaged 0.7.0 pin; a local file remains an optional override. Co-authored-by: hardlydiff --- README.md | 9 ++- bin/canton-dev-tools | 2 +- config/default-splice-dars.json | 70 ++++++++++++++++++++ package.json | 3 +- src/cli.ts | 2 +- src/daml/check-dar-version-policy.ts | 10 ++- src/daml/dar-utils.ts | 8 +-- src/daml/prepare-build.ts | 12 +++- src/daml/sync-splice-dars.ts | 38 ++++++++--- src/daml/verify-dars.ts | 4 +- src/testing/testConfig.ts | 5 +- test/unit/cip56/transferSmokeTraffic.test.ts | 8 ++- test/unit/daml/packages.test.ts | 20 ++---- test/unit/daml/upgrade-and-policy.test.ts | 37 ++--------- test/unit/daml/verify-and-safety.test.ts | 51 +++++++++++++- test/unit/scripts/package-artifacts.test.ts | 1 + 16 files changed, 196 insertions(+), 84 deletions(-) create mode 100644 config/default-splice-dars.json diff --git a/README.md b/README.md index 8c60dd3..c06c63d 100644 --- a/README.md +++ b/README.md @@ -39,14 +39,19 @@ 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` (Splice **0.7.0** +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..42b2411 --- /dev/null +++ b/config/default-splice-dars.json @@ -0,0 +1,70 @@ +{ + "spliceRef": "a9076eb91f87a9bd9315d2f9e122d6350bdc9d4c", + "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.json b/package.json index 246710e..a1b2017 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fairmint/canton-dev-tools", - "version": "0.1.0", + "version": "0.1.3", "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/**', From aa7bbb2e9152abe711b565eb5d07d41fc88e6bd2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 11 Aug 2026 19:38:39 +0000 Subject: [PATCH 2/2] Pin default splice DARs to MainNet 0.6.14 and bump to 0.1.4 Replace TestNet 0.7.0 spliceRef with MainNet commit 398919a5b13479877fd61587003ba7a4ba00091b (tag 0.6.14). DAR sha256s are unchanged. Bump package to 0.1.4 so main merge publishes the fix (0.1.3 already shipped with the wrong pin). Co-authored-by: hardlydiff --- README.md | 5 +++-- config/default-splice-dars.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c06c63d..6a64a69 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,9 @@ npx canton-dev-tools sync-splice-dars ### `sync-splice-dars` config -By default, sync uses the packaged pin at `config/default-splice-dars.json` (Splice **0.7.0** -token-standard + amulet DARs). Optional overrides, in order: +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` diff --git a/config/default-splice-dars.json b/config/default-splice-dars.json index 42b2411..b962396 100644 --- a/config/default-splice-dars.json +++ b/config/default-splice-dars.json @@ -1,5 +1,5 @@ { - "spliceRef": "a9076eb91f87a9bd9315d2f9e122d6350bdc9d4c", + "spliceRef": "398919a5b13479877fd61587003ba7a4ba00091b", "requiredDars": [ { "file": "splice-amulet-0.1.16.dar", 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 a1b2017..08edba5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@fairmint/canton-dev-tools", - "version": "0.1.3", + "version": "0.1.4", "description": "Shared LocalNet CLI, DAML package tooling, and Canton integration-test helpers for Fairmint", "keywords": [ "canton",