Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cli command update aztec dependencies #3128

Merged
merged 17 commits into from
Nov 2, 2023
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
1 change: 0 additions & 1 deletion yarn-project/acir-simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/cli/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/cli/aztec-cli
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions yarn-project/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/cli/src/github.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const GITHUB_OWNER = 'AztecProtocol';
export const GITHUB_REPO = 'aztec-packages';
export const GITHUB_TAG_PREFIX = 'aztec-packages';
19 changes: 16 additions & 3 deletions yarn-project/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 <string>', 'URL of the PXE')
.env('PXE_URL')
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 <semver>', '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);
Expand Down
5 changes: 2 additions & 3 deletions yarn-project/cli/src/unbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand Down
16 changes: 16 additions & 0 deletions yarn-project/cli/src/update/common.ts
Original file line number Diff line number Diff line change
@@ -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;
}>;
};
79 changes: 79 additions & 0 deletions yarn-project/cli/src/update/noir.ts
Original file line number Diff line number Diff line change
@@ -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<DependencyChanges> {
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',
},
);
}
129 changes: 129 additions & 0 deletions yarn-project/cli/src/update/npm.ts
Original file line number Diff line number Diff line change
@@ -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<string, string>;
}> {
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<SemVer> {
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<DependencyChanges> {
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...`);
}
}
Loading