diff --git a/yarn-project/acir-simulator/package.json b/yarn-project/acir-simulator/package.json index 74ff55000ce..c1a7dda3a84 100644 --- a/yarn-project/acir-simulator/package.json +++ b/yarn-project/acir-simulator/package.json @@ -49,7 +49,6 @@ "@types/node": "^18.7.23", "jest": "^29.5.0", "jest-mock-extended": "^3.0.4", - "toml": "^3.0.0", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", "typescript": "^5.0.4", diff --git a/yarn-project/cli/Dockerfile b/yarn-project/cli/Dockerfile index 1d8608c7861..29ef3f559ea 100644 --- a/yarn-project/cli/Dockerfile +++ b/yarn-project/cli/Dockerfile @@ -23,6 +23,8 @@ ENV XDG_CACHE_HOME /cache RUN mkdir /cache && chmod 777 /cache VOLUME [ "/cache" ] +RUN corepack enable + # run as non-root user RUN addgroup -S aztec && adduser -S aztec -G aztec USER aztec diff --git a/yarn-project/cli/aztec-cli b/yarn-project/cli/aztec-cli index 6fa3e72b27e..38132243049 100755 --- a/yarn-project/cli/aztec-cli +++ b/yarn-project/cli/aztec-cli @@ -115,6 +115,11 @@ if [[ "$AZTEC_CLI_COMMAND" == "unbox" ]]; then add_mount "$DIR" fi +if [[ "$AZTEC_CLI_COMMAND" == "update" ]]; then + # update command defaults to current directory + add_mount "$PWD" +fi + # process flags if [[ "$AZTEC_CLI_COMMAND" == "compile" || "$AZTEC_CLI_COMMAND" == "call" || "$AZTEC_CLI_COMMAND" == "send" ]]; then diff --git a/yarn-project/cli/package.json b/yarn-project/cli/package.json index 21630e4a2c0..f1304f5fe44 100644 --- a/yarn-project/cli/package.json +++ b/yarn-project/cli/package.json @@ -42,6 +42,7 @@ "@aztec/noir-contracts": "workspace:^", "@aztec/types": "workspace:^", "@libp2p/peer-id-factory": "^3.0.4", + "@ltd/j-toml": "^1.38.0", "commander": "^9.0.0", "jszip": "^3.10.1", "lodash.startcase": "^4.4.0", diff --git a/yarn-project/cli/src/github.ts b/yarn-project/cli/src/github.ts new file mode 100644 index 00000000000..0486c382369 --- /dev/null +++ b/yarn-project/cli/src/github.ts @@ -0,0 +1,3 @@ +export const GITHUB_OWNER = 'AztecProtocol'; +export const GITHUB_REPO = 'aztec-packages'; +export const GITHUB_TAG_PREFIX = 'aztec-packages'; diff --git a/yarn-project/cli/src/index.ts b/yarn-project/cli/src/index.ts index 2dd5ae41739..2b2ec4834c2 100644 --- a/yarn-project/cli/src/index.ts +++ b/yarn-project/cli/src/index.ts @@ -33,6 +33,7 @@ import { mnemonicToAccount } from 'viem/accounts'; import { createCompatibleClient } from './client.js'; import { encodeArgs, parseStructString } from './encoding.js'; import { unboxContract } from './unbox.js'; +import { update } from './update/update.js'; import { deployAztecContracts, getContractArtifact, @@ -70,9 +71,9 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { const program = new Command(); const packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), '../package.json'); - const version: string = JSON.parse(readFileSync(packageJsonPath).toString()).version; + const cliVersion: string = JSON.parse(readFileSync(packageJsonPath).toString()).version; - program.name('aztec-cli').description('CLI for interacting with Aztec.').version(version); + program.name('aztec-cli').description('CLI for interacting with Aztec.').version(cliVersion); const pxeOption = new Option('-u, --rpc-url ', 'URL of the PXE') .env('PXE_URL') @@ -649,7 +650,7 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { ) .action(async (contractName, localDirectory) => { const unboxTo: string = localDirectory ? localDirectory : contractName; - await unboxContract(contractName, unboxTo, version, log); + await unboxContract(contractName, unboxTo, cliVersion, log); }); program @@ -701,6 +702,18 @@ export function getProgram(log: LogFn, debugLogger: DebugLogger): Command { log(`${selector}`); }); + program + .command('update') + .description('Updates Nodejs and Noir dependencies') + .argument('[projectPath]', 'Path to the project directory', process.cwd()) + .option('--contract [paths...]', 'Paths to contracts to update dependencies', []) + .option('--sandbox-version ', 'The sandbox version to update to. Defaults to latest', 'latest') + .addOption(pxeOption) + .action(async (projectPath: string, options) => { + const { contract } = options; + await update(projectPath, contract, options.rpcUrl, options.sandboxVersion, log, debugLogger); + }); + compileContract(program, 'compile', log); generateTypescriptInterface(program, 'generate-typescript', log); generateNoirInterface(program, 'generate-noir-interface', log); diff --git a/yarn-project/cli/src/unbox.ts b/yarn-project/cli/src/unbox.ts index 07fcdd21983..7cb010f3e3c 100644 --- a/yarn-project/cli/src/unbox.ts +++ b/yarn-project/cli/src/unbox.ts @@ -14,9 +14,8 @@ import JSZip from 'jszip'; import fetch from 'node-fetch'; import * as path from 'path'; -const GITHUB_OWNER = 'AztecProtocol'; -const GITHUB_REPO = 'aztec-packages'; -const GITHUB_TAG_PREFIX = 'aztec-packages'; +import { GITHUB_OWNER, GITHUB_REPO, GITHUB_TAG_PREFIX } from './github.js'; + const BOXES_PATH = 'yarn-project/boxes'; /** diff --git a/yarn-project/cli/src/update/common.ts b/yarn-project/cli/src/update/common.ts new file mode 100644 index 00000000000..6041c1266e4 --- /dev/null +++ b/yarn-project/cli/src/update/common.ts @@ -0,0 +1,16 @@ +/** + * Tracks changes to dependencies + */ +export type DependencyChanges = { + /** Which file was changed */ + file: string; + /** changes done to the file */ + dependencies: Array<{ + /** Name of the dependency being changed */ + name: string; + /** Previous version of the dependency */ + from: string; + /** New version of the dependency (after the update) */ + to: string; + }>; +}; diff --git a/yarn-project/cli/src/update/noir.ts b/yarn-project/cli/src/update/noir.ts new file mode 100644 index 00000000000..a86703e4e0d --- /dev/null +++ b/yarn-project/cli/src/update/noir.ts @@ -0,0 +1,79 @@ +import { LogFn } from '@aztec/foundation/log'; +import { NoirPackageConfig, parseNoirPackageConfig } from '@aztec/foundation/noir'; + +import TOML from '@ltd/j-toml'; +import { readFile } from 'fs/promises'; +import { EOL } from 'os'; +import { join, relative, resolve } from 'path'; + +import { atomicUpdateFile } from '../utils.js'; +import { DependencyChanges } from './common.js'; + +/** + * Updates Aztec.nr dependencies + * @param contractPath - Path to the contract to be updated + * @param tag - The tag to update to + * @param log - Logging function + */ +export async function updateAztecNr(contractPath: string, tag: string, log: LogFn): Promise { + const configFilepath = resolve(join(contractPath, 'Nargo.toml')); + const packageConfig = parseNoirPackageConfig(TOML.parse(await readFile(configFilepath, 'utf-8'))); + const changes: DependencyChanges = { + dependencies: [], + file: configFilepath, + }; + + log(`Updating Aztec.nr libraries to ${tag} in ${relative(process.cwd(), changes.file)}`); + for (const dep of Object.values(packageConfig.dependencies)) { + if (!('git' in dep)) { + continue; + } + + // remove trailing slash + const gitUrl = dep.git.toLowerCase().replace(/\/$/, ''); + if (gitUrl !== 'https://github.com/aztecprotocol/aztec-packages') { + continue; + } + + if (dep.tag !== tag) { + // show the Aztec.nr package name rather than the lib name + const dirParts = dep.directory?.split('/') ?? []; + changes.dependencies.push({ + name: dirParts.slice(-2).join('/'), + from: dep.tag, + to: tag, + }); + + dep.tag = tag; + } + } + + if (changes.dependencies.length > 0) { + const contents = prettyPrintTOML(packageConfig); + await atomicUpdateFile(configFilepath, contents); + } + + return changes; +} + +/** + * Pretty prints a NoirPackageConfig to a string + * @param packageConfig - Nargo.toml contents + * @returns The Nargo.toml contents as a string + */ +function prettyPrintTOML(packageConfig: NoirPackageConfig): string { + // hint to TOML.stringify how we want the file to look like + return TOML.stringify( + { + package: TOML.Section(packageConfig.package), + dependencies: TOML.Section( + Object.fromEntries(Object.entries(packageConfig.dependencies).map(([name, dep]) => [name, TOML.inline(dep)])), + ), + }, + { + indent: 2, + newline: EOL as any, + newlineAround: 'section', + }, + ); +} diff --git a/yarn-project/cli/src/update/npm.ts b/yarn-project/cli/src/update/npm.ts new file mode 100644 index 00000000000..41dadbf518e --- /dev/null +++ b/yarn-project/cli/src/update/npm.ts @@ -0,0 +1,129 @@ +import { LogFn } from '@aztec/foundation/log'; + +import { spawnSync } from 'child_process'; +import { existsSync } from 'fs'; +import { readFile } from 'fs/promises'; +import { join, relative, resolve } from 'path'; +import { SemVer, parse } from 'semver'; + +import { atomicUpdateFile } from '../utils.js'; +import { DependencyChanges } from './common.js'; + +/** + * Looks up a package.json file and returns its contents + * @param projectPath - Path to Nodejs project + * @returns The parsed package.json + */ +export async function readPackageJson(projectPath: string): Promise<{ + /** dependencies */ + dependencies?: Record; +}> { + const configFilepath = resolve(join(projectPath, 'package.json')); + const pkg = JSON.parse(await readFile(configFilepath, 'utf-8')); + + return pkg; +} + +/** + * Queries the npm registry for the latest version of a package + * @param packageName - The package to query + * @param distTag - The distribution tag + * @returns The latest version of the package on that distribution tag + */ +export async function getNewestVersion(packageName: string, distTag = 'latest'): Promise { + const url = new URL(packageName, 'https://registry.npmjs.org/'); + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch ${url}`); + } + + const body = await response.json(); + const latestVersion = parse(body['dist-tags'][distTag]); + if (!latestVersion) { + throw new Error(`Failed to get latest version from registry`); + } + + return latestVersion; +} + +/** + * Updates a project's \@aztec/* dependencies to the specific version + * @param projectPath - Path to Nodejs project + * @param aztecVersion - The version to update to + * @returns True if the project was updated + */ +export async function updateAztecDeps( + projectPath: string, + aztecVersion: SemVer, + log: LogFn, +): Promise { + const pkg = await readPackageJson(projectPath); + const changes: DependencyChanges = { + file: resolve(join(projectPath, 'package.json')), + dependencies: [], + }; + + if (!pkg.dependencies) { + return changes; + } + + log(`Updating @aztec packages to ${aztecVersion} in ${relative(process.cwd(), changes.file)}`); + const version = aztecVersion.version; + + for (const name of Object.keys(pkg.dependencies)) { + if (!name.startsWith('@aztec/')) { + continue; + } + + // different release schedule + if (name === '@aztec/aztec-ui') { + continue; + } + + if (pkg.dependencies[name] !== version) { + changes.dependencies.push({ + name, + from: pkg.dependencies[name], + to: version, + }); + + pkg.dependencies[name] = version; + } + } + + if (changes.dependencies.length > 0) { + const contents = JSON.stringify(pkg, null, 2) + '\n'; + await atomicUpdateFile(resolve(join(projectPath, 'package.json')), contents); + } + + return changes; +} + +/** + * Updates a project's yarn.lock or package-lock.json + * @param projectPath - Path to Nodejs project + */ +export function updateLockfile(projectPath: string, log: LogFn): void { + const isNpm = existsSync(resolve(join(projectPath, 'package-lock.json'))); + const isYarn = existsSync(resolve(join(projectPath, 'yarn.lock'))); + const isPnpm = existsSync(resolve(join(projectPath, 'pnpm-lock.yaml'))); + + if (isPnpm) { + spawnSync('pnpm', ['install'], { + cwd: projectPath, + stdio: 'inherit', + }); + } else if (isYarn) { + spawnSync('yarn', ['install'], { + cwd: projectPath, + stdio: 'inherit', + }); + } else if (isNpm) { + spawnSync('npm', ['install'], { + cwd: projectPath, + stdio: 'inherit', + }); + } else { + log(`No lockfile found in ${projectPath}. Skipping lockfile update...`); + } +} diff --git a/yarn-project/cli/src/update/update.ts b/yarn-project/cli/src/update/update.ts new file mode 100644 index 00000000000..3a5940a44da --- /dev/null +++ b/yarn-project/cli/src/update/update.ts @@ -0,0 +1,125 @@ +/* eslint-disable jsdoc/require-jsdoc */ +import { DebugLogger, LogFn } from '@aztec/foundation/log'; + +import { relative, resolve } from 'path'; +import { SemVer, coerce, gt, lt, parse } from 'semver'; + +import { createCompatibleClient } from '../client.js'; +import { GITHUB_TAG_PREFIX } from '../github.js'; +import { DependencyChanges } from './common.js'; +import { updateAztecNr } from './noir.js'; +import { getNewestVersion as getLatestVersion, readPackageJson, updateAztecDeps, updateLockfile } from './npm.js'; + +const SANDBOX_PACKAGE = '@aztec/aztec-sandbox'; + +export async function update( + projectPath: string, + contracts: string[], + pxeUrl: string, + sandboxVersion: string, + log: LogFn, + debugLog: DebugLogger, +): Promise { + const targetSandboxVersion = + sandboxVersion === 'latest' ? await getLatestVersion(SANDBOX_PACKAGE, 'latest') : parse(sandboxVersion); + + if (!targetSandboxVersion) { + throw new Error(`Invalid aztec version ${sandboxVersion}`); + } + + let currentSandboxVersion = await getNpmSandboxVersion(projectPath, log); + + if (!currentSandboxVersion) { + currentSandboxVersion = await getRemoteSandboxVersion(pxeUrl, log, debugLog); + + if (currentSandboxVersion && lt(currentSandboxVersion, targetSandboxVersion)) { + log(` +Sandbox is older than version ${targetSandboxVersion}. If running in docker update it with the following command then restart the container: +docker pull aztecprotocol/aztec-sandbox:latest +Once the container is restarted, run the \`aztec-cli update\` command again`); + return; + } + } + + if (!currentSandboxVersion) { + throw new Error('Sandbox version could not be detected'); + } + + // sanity check + if (gt(currentSandboxVersion, targetSandboxVersion)) { + throw new Error('Local sandbox version is newer than latest version.'); + } + + const npmChanges = await updateAztecDeps(projectPath, targetSandboxVersion, log); + if (npmChanges.dependencies.length > 0) { + updateLockfile(projectPath, log); + } + + const contractChanges: DependencyChanges[] = []; + for (const contract of contracts) { + try { + contractChanges.push( + await updateAztecNr( + resolve(projectPath, contract), + `${GITHUB_TAG_PREFIX}-v${targetSandboxVersion.version}`, + log, + ), + ); + } catch (err) { + if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { + log(`No Nargo.toml found in ${relative(process.cwd(), contract)}. Skipping...`); + process.exit(1); + } + + throw err; + } + } + + printChanges(npmChanges, log); + + contractChanges.forEach(changes => { + printChanges(changes, log); + }); +} + +function printChanges(changes: DependencyChanges, log: LogFn): void { + log(`\nIn ${relative(process.cwd(), changes.file)}:`); + if (changes.dependencies.length === 0) { + log(' No changes'); + } else { + changes.dependencies.forEach(({ name, from, to }) => { + log(` Updated ${name} from ${from} to ${to}`); + }); + } +} + +async function getNpmSandboxVersion(projectPath: string, log: LogFn): Promise { + try { + const pkg = await readPackageJson(projectPath); + // use coerce instead of parse because it eliminates semver operators like ~ and ^ + return coerce(pkg.dependencies?.[SANDBOX_PACKAGE]); + } catch (err) { + if (err instanceof Error && 'code' in err && err.code === 'ENOENT') { + log(`No package.json found in ${projectPath}`); + process.exit(1); + } + + throw err; + } +} + +async function getRemoteSandboxVersion(pxeUrl: string, log: LogFn, debugLog: DebugLogger): Promise { + try { + const client = await createCompatibleClient(pxeUrl, debugLog); + const nodeInfo = await client.getNodeInfo(); + + return parse(nodeInfo.sandboxVersion); + } catch (err) { + if (err instanceof Error && err.message === 'fetch failed') { + log(`Could not connect to Sandbox running on ${pxeUrl}`); + process.exit(1); + } + + throw err; + } +} diff --git a/yarn-project/cli/src/utils.ts b/yarn-project/cli/src/utils.ts index 6c9d81cdae5..9973d9fa2fc 100644 --- a/yarn-project/cli/src/utils.ts +++ b/yarn-project/cli/src/utils.ts @@ -16,7 +16,7 @@ import { } from '@aztec/l1-artifacts'; import { LogId } from '@aztec/types'; -import { InvalidArgumentError } from 'commander'; +import { CommanderError, InvalidArgumentError } from 'commander'; import fs from 'fs'; import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts'; @@ -403,3 +403,31 @@ export function parseField(field: string): Fr { export function parseFields(fields: string[]): Fr[] { return fields.map(parseField); } + +/** + * Updates a file in place atomically. + * @param filePath - Path to file + * @param contents - New contents to write + */ +export async function atomicUpdateFile(filePath: string, contents: string) { + const tmpFilepath = filePath + '.tmp'; + try { + await fs.promises.writeFile(tmpFilepath, contents, { + // let's crash if the tmp file already exists + flag: 'wx', + }); + await fs.promises.rename(tmpFilepath, filePath); + } catch (e) { + if (e instanceof Error && 'code' in e && e.code === 'EEXIST') { + const commanderError = new CommanderError( + 1, + e.code, + `Temporary file already exists: ${tmpFilepath}. Delete this file and try again.`, + ); + commanderError.nestedError = e.message; + throw commanderError; + } else { + throw e; + } + } +} diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index 9ac562c62f1..ef597d2b308 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -33,7 +33,8 @@ "./bigint-buffer": "./dest/bigint-buffer/index.js", "./types": "./dest/types/index.js", "./url": "./dest/url/index.js", - "./committable": "./dest/committable/index.js" + "./committable": "./dest/committable/index.js", + "./noir": "./dest/noir/index.js" }, "scripts": { "build": "yarn clean && tsc -b", @@ -69,7 +70,8 @@ "lodash.clonedeepwith": "^4.5.0", "memdown": "^6.1.1", "pako": "^2.1.0", - "sha3": "^2.1.4" + "sha3": "^2.1.4", + "zod": "^3.22.4" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/yarn-project/foundation/src/noir/index.ts b/yarn-project/foundation/src/noir/index.ts new file mode 100644 index 00000000000..e91eaa5cfec --- /dev/null +++ b/yarn-project/foundation/src/noir/index.ts @@ -0,0 +1 @@ +export * from './noir_package_config.js'; diff --git a/yarn-project/foundation/src/noir/noir_package_config.ts b/yarn-project/foundation/src/noir/noir_package_config.ts new file mode 100644 index 00000000000..a1cd84f37e1 --- /dev/null +++ b/yarn-project/foundation/src/noir/noir_package_config.ts @@ -0,0 +1,54 @@ +import { z } from 'zod'; + +const noirGitDependencySchema = z.object({ + git: z.string(), + tag: z.string(), + directory: z.string().optional(), +}); + +const noirLocalDependencySchema = z.object({ + path: z.string(), +}); + +const noirPackageConfigSchema = z.object({ + package: z.object({ + name: z.string().default(''), + type: z.enum(['lib', 'contract', 'binary']).default('binary'), + entry: z.string().optional(), + description: z.string().optional(), + authors: z.array(z.string()).optional(), + // eslint-disable-next-line camelcase + compiler_version: z.string().optional(), + backend: z.string().optional(), + license: z.string().optional(), + }), + dependencies: z.record(z.union([noirGitDependencySchema, noirLocalDependencySchema])).default({}), +}); + +/** + * Noir package configuration. + */ +export type NoirPackageConfig = z.infer; + +/** + * A remote package dependency. + */ +export type NoirGitDependencyConfig = z.infer; + +/** + * A local package dependency. + */ +export type NoirLocalDependencyConfig = z.infer; + +/** + * A package dependency. + */ +export type NoirDependencyConfig = NoirGitDependencyConfig | NoirLocalDependencyConfig; + +/** + * Checks that an object is a package configuration. + * @param config - Config to check + */ +export function parseNoirPackageConfig(config: any): NoirPackageConfig { + return noirPackageConfigSchema.parse(config); +} diff --git a/yarn-project/noir-compiler/package.json b/yarn-project/noir-compiler/package.json index 03a4837ebd3..d0479220470 100644 --- a/yarn-project/noir-compiler/package.json +++ b/yarn-project/noir-compiler/package.json @@ -46,6 +46,7 @@ }, "dependencies": { "@aztec/foundation": "workspace:^", + "@ltd/j-toml": "^1.38.0", "@noir-lang/noir_wasm": "0.18.0-3919619.aztec", "@noir-lang/source-resolver": "0.18.0-3919619.aztec", "base64-js": "^1.5.1", @@ -58,10 +59,8 @@ "lodash.upperfirst": "^4.3.1", "memfs": "^4.6.0", "pako": "^2.1.0", - "toml": "^3.0.0", "tslib": "^2.4.0", - "unzipit": "^1.4.3", - "zod": "^3.22.4" + "unzipit": "^1.4.3" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts index 42b53732733..1c32916c075 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.test.ts @@ -1,4 +1,5 @@ -import { NoirDependencyConfig } from '../package-config.js'; +import { NoirDependencyConfig } from '@aztec/foundation/noir'; + import { NoirPackage } from '../package.js'; import { NoirDependencyManager } from './dependency-manager.js'; import { DependencyResolver } from './dependency-resolver.js'; diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts index 1be0002cb95..ac5220e907f 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-manager.ts @@ -1,8 +1,8 @@ import { LogFn, createDebugOnlyLogger } from '@aztec/foundation/log'; +import { NoirDependencyConfig } from '@aztec/foundation/noir'; import { join } from 'node:path'; -import { NoirDependencyConfig } from '../package-config.js'; import { NoirPackage } from '../package.js'; import { DependencyResolver } from './dependency-resolver.js'; diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts index af55dc21a2d..b4994190ed5 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/dependency-resolver.ts @@ -1,4 +1,5 @@ -import { NoirDependencyConfig } from '../package-config.js'; +import { NoirDependencyConfig } from '@aztec/foundation/noir'; + import { NoirPackage } from '../package.js'; /** diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts index e369c63f5ed..4b6703484ab 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.test.ts @@ -1,3 +1,4 @@ +import { NoirGitDependencyConfig } from '@aztec/foundation/noir'; import { fileURLToPath } from '@aztec/foundation/url'; import { jest } from '@jest/globals'; @@ -7,7 +8,6 @@ import { dirname, join } from 'node:path'; import { FileManager } from '../file-manager/file-manager.js'; import { createMemFSFileManager } from '../file-manager/memfs-file-manager.js'; -import { NoirGitDependencyConfig } from '../package-config.js'; import { NoirPackage } from '../package.js'; import { DependencyResolver } from './dependency-resolver.js'; import { GithubDependencyResolver, resolveGithubCodeArchive, safeFilename } from './github-dependency-resolver.js'; @@ -87,6 +87,7 @@ describe('GithubDependencyResolver', () => { [ { git: 'https://github.com/example/lib.nr', + tag: 'HEAD', }, 'zip', 'https://github.com/example/lib.nr/archive/HEAD.zip', @@ -94,6 +95,7 @@ describe('GithubDependencyResolver', () => { [ { git: 'https://github.com/example/lib.nr', + tag: 'HEAD', }, 'tar', 'https://github.com/example/lib.nr/archive/HEAD.tar.gz', @@ -103,12 +105,13 @@ describe('GithubDependencyResolver', () => { expect(archiveUrl.href).toEqual(href); }); - it.each([{ git: 'https://github.com/' }, { git: 'https://github.com/foo' }, { git: 'https://example.com' }])( - 'throws if the Github URL is invalid', - dep => { - expect(() => resolveGithubCodeArchive(dep, 'zip')).toThrow(); - }, - ); + it.each([ + { git: 'https://github.com/', tag: 'v1' }, + { git: 'https://github.com/foo', tag: 'v1' }, + { git: 'https://example.com', tag: 'v1' }, + ])('throws if the Github URL is invalid', dep => { + expect(() => resolveGithubCodeArchive(dep, 'zip')).toThrow(); + }); it.each([ ['main', 'main'], diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts index 5079fe4f766..36bbcbc3762 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/github-dependency-resolver.ts @@ -1,10 +1,10 @@ import { createDebugOnlyLogger } from '@aztec/foundation/log'; +import { NoirDependencyConfig, NoirGitDependencyConfig } from '@aztec/foundation/noir'; import { delimiter, join, sep } from 'node:path'; import { unzip } from 'unzipit'; import { FileManager } from '../file-manager/file-manager.js'; -import { NoirDependencyConfig, NoirGitDependencyConfig } from '../package-config.js'; import { NoirPackage } from '../package.js'; import { DependencyResolver } from './dependency-resolver.js'; diff --git a/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts b/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts index 481c338a416..29862ef2470 100644 --- a/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts +++ b/yarn-project/noir-compiler/src/compile/noir/dependencies/local-dependency-resolver.ts @@ -1,7 +1,8 @@ +import { NoirDependencyConfig } from '@aztec/foundation/noir'; + import { resolve } from 'path'; import { FileManager } from '../file-manager/file-manager.js'; -import { NoirDependencyConfig } from '../package-config.js'; import { NoirPackage } from '../package.js'; import { DependencyResolver } from './dependency-resolver.js'; diff --git a/yarn-project/noir-compiler/src/compile/noir/package-config.ts b/yarn-project/noir-compiler/src/compile/noir/package-config.ts deleted file mode 100644 index fa412b476ce..00000000000 --- a/yarn-project/noir-compiler/src/compile/noir/package-config.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { z } from 'zod'; - -const noirGitDependency = z.object({ - git: z.string(), - tag: z.string().optional(), - directory: z.string().optional(), -}); - -const noirLocalDependency = z.object({ - path: z.string(), -}); - -const noirPackageConfig = z.object({ - package: z.object({ - name: z.string(), - type: z.enum(['lib', 'contract', 'binary']), - }), - dependencies: z.record(z.union([noirGitDependency, noirLocalDependency])), -}); - -/** - * Noir package configuration. - */ -export type NoirPackageConfig = z.infer; - -/** - * A remote package dependency. - */ -export type NoirGitDependencyConfig = z.infer; - -/** - * A local package dependency. - */ -export type NoirLocalDependencyConfig = z.infer; - -/** - * A package dependency. - */ -export type NoirDependencyConfig = NoirGitDependencyConfig | NoirLocalDependencyConfig; - -/** - * Checks that an object is a package configuration. - * @param config - Config to check - */ -export function parsePackageConfig(config: any): NoirPackageConfig { - return noirPackageConfig.parse(config); -} diff --git a/yarn-project/noir-compiler/src/compile/noir/package.ts b/yarn-project/noir-compiler/src/compile/noir/package.ts index bdc00b2e34c..9a295c11d8c 100644 --- a/yarn-project/noir-compiler/src/compile/noir/package.ts +++ b/yarn-project/noir-compiler/src/compile/noir/package.ts @@ -1,13 +1,9 @@ +import { NoirDependencyConfig, NoirPackageConfig, parseNoirPackageConfig } from '@aztec/foundation/noir'; + +import { parse } from '@ltd/j-toml'; import { join } from 'node:path'; -import { parse as parseToml } from 'toml'; import { FileManager } from './file-manager/file-manager.js'; -import { - NoirGitDependencyConfig, - NoirLocalDependencyConfig, - NoirPackageConfig, - parsePackageConfig, -} from './package-config.js'; const CONFIG_FILE_NAME = 'Nargo.toml'; @@ -71,7 +67,7 @@ export class NoirPackage { /** * Gets this package's dependencies. */ - public getDependencies(): Record { + public getDependencies(): Record { return this.#config.dependencies; } @@ -83,7 +79,7 @@ export class NoirPackage { */ public static open(path: string, fm: FileManager): NoirPackage { const fileContents = fm.readFileSync(join(path, CONFIG_FILE_NAME), 'utf-8'); - const config = parsePackageConfig(parseToml(fileContents)); + const config = parseNoirPackageConfig(parse(fileContents)); return new NoirPackage(path, join(path, 'src'), config); } diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index 7a328c4143d..e46fc104a11 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -77,7 +77,6 @@ __metadata: jest-mock-extended: ^3.0.4 levelup: ^5.1.1 memdown: ^6.1.1 - toml: ^3.0.0 ts-jest: ^29.1.0 ts-node: ^10.9.1 tslib: ^2.4.0 @@ -366,6 +365,7 @@ __metadata: "@aztec/types": "workspace:^" "@jest/globals": ^29.5.0 "@libp2p/peer-id-factory": ^3.0.4 + "@ltd/j-toml": ^1.38.0 "@rushstack/eslint-patch": ^1.1.4 "@types/jest": ^29.5.0 "@types/lodash.startcase": ^4.4.7 @@ -522,6 +522,7 @@ __metadata: ts-jest: ^29.1.0 ts-node: ^10.9.1 typescript: ^5.0.4 + zod: ^3.22.4 languageName: unknown linkType: soft @@ -586,6 +587,7 @@ __metadata: dependencies: "@aztec/foundation": "workspace:^" "@jest/globals": ^29.5.0 + "@ltd/j-toml": ^1.38.0 "@noir-lang/noir_wasm": 0.18.0-3919619.aztec "@noir-lang/source-resolver": 0.18.0-3919619.aztec "@rushstack/eslint-patch": ^1.1.4 @@ -609,13 +611,11 @@ __metadata: lodash.upperfirst: ^4.3.1 memfs: ^4.6.0 pako: ^2.1.0 - toml: ^3.0.0 ts-jest: ^29.1.0 ts-node: ^10.9.1 tslib: ^2.4.0 typescript: ^5.0.4 unzipit: ^1.4.3 - zod: ^3.22.4 bin: aztec-compile: dest/cli.js languageName: unknown @@ -3237,6 +3237,13 @@ __metadata: languageName: node linkType: hard +"@ltd/j-toml@npm:^1.38.0": + version: 1.38.0 + resolution: "@ltd/j-toml@npm:1.38.0" + checksum: 34f5d0ec652e790a7a733f0d3a8d9957d63997bd0efc13a61beb9d772bae75519453884fbc3fd6a2d5fe15674834bdd57ca1824bb1de8f829e5ce195fc5fa3ea + languageName: node + linkType: hard + "@metamask/safe-event-emitter@npm:2.0.0, @metamask/safe-event-emitter@npm:^2.0.0": version: 2.0.0 resolution: "@metamask/safe-event-emitter@npm:2.0.0" @@ -18113,13 +18120,6 @@ __metadata: languageName: unknown linkType: soft -"toml@npm:^3.0.0": - version: 3.0.0 - resolution: "toml@npm:3.0.0" - checksum: 5d7f1d8413ad7780e9bdecce8ea4c3f5130dd53b0a4f2e90b93340979a137739879d7b9ce2ce05c938b8cc828897fe9e95085197342a1377dd8850bf5125f15f - languageName: node - linkType: hard - "toposort@npm:^2.0.2": version: 2.0.2 resolution: "toposort@npm:2.0.2"