From 6e3999f7b6239c1c14acf57298ec07beaf608c6f Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 29 May 2024 18:25:26 +0800 Subject: [PATCH 1/5] No need to export project --- packages/cli/src/index.ts | 1 - test/contract.test.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 55f27d54a..dbcd69449 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -20,4 +20,3 @@ export * from './types' export * from './utils' export * from './deployment' export * from './codegen' -export * from './project' diff --git a/test/contract.test.ts b/test/contract.test.ts index 8755d76aa..c86fa4817 100644 --- a/test/contract.test.ts +++ b/test/contract.test.ts @@ -61,7 +61,7 @@ import { getContractByCodeHash } from '../artifacts/ts/contracts' import { UserAccount, NFTTest, OwnerOnly, TokenTest, MapTest, UserAccountTypes } from '../artifacts/ts' import { randomBytes } from 'crypto' import { TokenBalance } from '../artifacts/ts/types' -import { ProjectArtifact, Project } from '@alephium/cli' +import { ProjectArtifact, Project } from '../packages/cli/src/project' describe('contract', function () { let signer: PrivateKeyWallet From cd8453e31f57e799b0961c20969557e82ec2dcc8 Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 29 May 2024 20:30:53 +0800 Subject: [PATCH 2/5] Add the forceRecompile flag --- gen.ts | 3 ++- packages/cli/cli_internal.ts | 9 ++++--- packages/cli/src/deployment.ts | 6 ++--- packages/cli/src/project.ts | 49 +++++++++++++++++++++------------- packages/cli/src/types.ts | 4 +-- packages/cli/src/utils.ts | 7 ++--- test/contract.test.ts | 4 ++- 7 files changed, 50 insertions(+), 32 deletions(-) diff --git a/gen.ts b/gen.ts index 2a0bee76a..73580c52c 100644 --- a/gen.ts +++ b/gen.ts @@ -16,7 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with the library. If not, see . */ -import { codegen, Project } from '@alephium/cli' +import { codegen } from '@alephium/cli' +import { Project } from './packages/cli/src/project' import { web3 } from '@alephium/web3' async function gen() { diff --git a/packages/cli/cli_internal.ts b/packages/cli/cli_internal.ts index d6bd65f59..c9063c5b6 100644 --- a/packages/cli/cli_internal.ts +++ b/packages/cli/cli_internal.ts @@ -28,7 +28,7 @@ import { codegen, getConfigFile, getSdkFullNodeVersion, - isDeployedOnMainnet, + isDeployed, isNetworkLive, loadConfig } from './src' @@ -103,14 +103,17 @@ program console.log(`Full node version: ${connectedFullNodeVersion}`) const cwd = path.resolve(process.cwd()) - const skipSaveArtifacts = config.skipSaveArtifacts || isDeployedOnMainnet(config) + const isContractDeployed = isDeployed(config) + if (!config.forceRecompile && isContractDeployed) { + console.warn(`The contracts has been deployed on testnet/mainnet, and the artifacts will not be updated.`) + } const project = await Project.compile( config.compilerOptions, cwd, config.sourceDir, config.artifactDir, connectedFullNodeVersion, - skipSaveArtifacts + config.forceRecompile || !isContractDeployed ) console.log('✅ Compilation completed!') if (options.skipGenerate) { diff --git a/packages/cli/src/deployment.ts b/packages/cli/src/deployment.ts index 27702141d..d718fc07a 100644 --- a/packages/cli/src/deployment.ts +++ b/packages/cli/src/deployment.ts @@ -55,7 +55,7 @@ import { getConfigFile, getDeploymentFilePath, getNetwork, - isDeployedOnMainnet, + isDeployed, loadConfig, retryFetch, taskIdToVariable, @@ -575,14 +575,14 @@ export async function deploy( const artifactDir = configuration.artifactDir ?? DEFAULT_CONFIGURATION_VALUES.artifactDir let project: Project | undefined = undefined if (configuration.skipRecompile !== true) { - const skipSaveArtifacts = configuration.skipSaveArtifacts || isDeployedOnMainnet(configuration) + const forceRecompile = configuration.forceRecompile || !isDeployed(configuration) project = await Project.compile( configuration.compilerOptions, path.resolve(process.cwd()), configuration.sourceDir ?? DEFAULT_CONFIGURATION_VALUES.sourceDir, artifactDir, undefined, - skipSaveArtifacts + forceRecompile ) } diff --git a/packages/cli/src/project.ts b/packages/cli/src/project.ts index a2c9623d5..4b81fa71e 100644 --- a/packages/cli/src/project.ts +++ b/packages/cli/src/project.ts @@ -365,16 +365,20 @@ export class Project { contracts: Map>, scripts: Map>, changedSources: SourceInfo[], - skipSaveArtifacts: boolean, + forceRecompile: boolean, errorOnWarnings: boolean ): void { const warnings: string[] = [] contracts.forEach((contract) => { - if (!skipSaveArtifacts || changedSources.find((s) => s.name === contract.sourceInfo.name) !== undefined) { + if (Project.needToUpdate(forceRecompile, changedSources, contract.sourceInfo.name)) { warnings.push(...contract.warnings) } }) - scripts.forEach((script) => warnings.push(...script.warnings)) + scripts.forEach((script) => { + if (Project.needToUpdate(forceRecompile, changedSources, script.sourceInfo.name)) { + warnings.push(...script.warnings) + } + }) if (warnings.length !== 0) { const prefixPerWarning = ' - ' const warningString = prefixPerWarning + warnings.join('\n' + prefixPerWarning) @@ -421,9 +425,13 @@ export class Project { return fsPromises.writeFile(filePath, JSON.stringify(structs, null, 2)) } + private static needToUpdate(forceRecompile: boolean, changedSources: SourceInfo[], name: string): boolean { + return forceRecompile || changedSources.find((s) => s.name === name) !== undefined + } + private async saveArtifactsToFile( projectRootDir: string, - skipSaveArtifacts: boolean, + forceRecompile: boolean, changedSources: SourceInfo[] ): Promise { const artifactsRootDir = this.artifactsRootDir @@ -436,20 +444,22 @@ export class Project { return fsPromises.writeFile(artifactPath, compiled.artifact.toString()) } for (const [_, contract] of this.contracts) { - if (!skipSaveArtifacts || changedSources.find((s) => s.name === contract.sourceInfo.name) !== undefined) { + if (Project.needToUpdate(forceRecompile, changedSources, contract.sourceInfo.name)) { await saveToFile(contract) } } for (const [_, script] of this.scripts) { - await saveToFile(script) + if (Project.needToUpdate(forceRecompile, changedSources, script.sourceInfo.name)) { + await saveToFile(script) + } } await this.saveStructsToFile() - await this.saveProjectArtifact(projectRootDir, skipSaveArtifacts, changedSources) + await this.saveProjectArtifact(projectRootDir, forceRecompile, changedSources) } - private async saveProjectArtifact(projectRootDir: string, skipSaveArtifacts: boolean, changedSources: SourceInfo[]) { - if (skipSaveArtifacts) { - // we should not update the `codeHashDebug` if the `skipSaveArtifacts` is enabled + private async saveProjectArtifact(projectRootDir: string, forceRecompile: boolean, changedSources: SourceInfo[]) { + if (!forceRecompile) { + // we should not update the `codeHashDebug` if the `forceRecompile` is disable const prevProjectArtifact = await ProjectArtifact.from(projectRootDir) if (prevProjectArtifact !== undefined) { for (const [name, info] of this.projectArtifact.infos) { @@ -516,7 +526,7 @@ export class Project { errorOnWarnings: boolean, compilerOptions: node.CompilerOptions, changedSources: SourceInfo[], - skipSaveArtifacts = false + forceRecompile: boolean ): Promise { const removeDuplicates = sourceInfos.reduce((acc: SourceInfo[], sourceInfo: SourceInfo) => { if (acc.find((info) => info.sourceCodeHash === sourceInfo.sourceCodeHash) === undefined) { @@ -558,7 +568,7 @@ export class Project { scripts, compilerOptions ) - Project.checkCompilerWarnings(contracts, scripts, changedSources, skipSaveArtifacts, errorOnWarnings) + Project.checkCompilerWarnings(contracts, scripts, changedSources, forceRecompile, errorOnWarnings) const project = new Project( contractsRootDir, artifactsRootDir, @@ -568,7 +578,7 @@ export class Project { structs, projectArtifact ) - await project.saveArtifactsToFile(projectRootDir, skipSaveArtifacts, changedSources) + await project.saveArtifactsToFile(projectRootDir, forceRecompile, changedSources) return project } @@ -581,7 +591,7 @@ export class Project { errorOnWarnings: boolean, compilerOptions: node.CompilerOptions, changedSources: SourceInfo[], - skipSaveArtifacts: boolean + forceRecompile: boolean ): Promise { const projectArtifact = await ProjectArtifact.from(projectRootDir) if (projectArtifact === undefined) { @@ -612,7 +622,7 @@ export class Project { } } - Project.checkCompilerWarnings(contracts, scripts, changedSources, skipSaveArtifacts, errorOnWarnings) + Project.checkCompilerWarnings(contracts, scripts, changedSources, forceRecompile, errorOnWarnings) return new Project(contractsRootDir, artifactsRootDir, sourceInfos, contracts, scripts, structs, projectArtifact) } catch (error) { console.log(`Failed to load artifacts, error: ${error}, try to re-compile contracts...`) @@ -625,7 +635,8 @@ export class Project { artifactsRootDir, errorOnWarnings, compilerOptions, - changedSources + changedSources, + forceRecompile ) } } @@ -749,7 +760,7 @@ export class Project { contractsRootDir = Project.DEFAULT_CONTRACTS_DIR, artifactsRootDir = Project.DEFAULT_ARTIFACTS_DIR, defaultFullNodeVersion: string | undefined = undefined, - skipSaveArtifacts = false + forceRecompile = false ): Promise { const provider = web3.getCurrentNodeProvider() const fullNodeVersion = defaultFullNodeVersion ?? (await provider.infos.getInfosVersion()).version @@ -776,7 +787,7 @@ export class Project { errorOnWarnings, nodeCompilerOptions, changedSources, - skipSaveArtifacts + forceRecompile ) } // we need to reload those contracts that did not regenerate bytecode @@ -789,7 +800,7 @@ export class Project { errorOnWarnings, nodeCompilerOptions, changedSources, - skipSaveArtifacts + forceRecompile ) } } diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index 89037b524..22ce58cfa 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -60,7 +60,7 @@ export interface Configuration { networks: Record> enableDebugMode?: boolean - skipSaveArtifacts?: boolean + forceRecompile?: boolean } export const DEFAULT_CONFIGURATION_VALUES = { @@ -87,7 +87,7 @@ export const DEFAULT_CONFIGURATION_VALUES = { confirmations: 2 } }, - skipSaveArtifacts: false + forceRecompile: false } export interface Environment { diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index fa9e74847..17f232ffa 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -107,9 +107,10 @@ export function getDeploymentFilePath(configuration: Configuration, networkId: N : path.join(configuration.artifactDir ?? DEFAULT_CONFIGURATION_VALUES.artifactDir, `.deployments.${networkId}.json`) } -export function isDeployedOnMainnet(configuration: Configuration): boolean { - const file = getDeploymentFilePath(configuration, 'mainnet') - return fs.existsSync(file) +export function isDeployed(configuration: Configuration): boolean { + const mainnet = getDeploymentFilePath(configuration, 'mainnet') + const testnet = getDeploymentFilePath(configuration, 'testnet') + return fs.existsSync(mainnet) || fs.existsSync(testnet) } export function getNetwork( diff --git a/test/contract.test.ts b/test/contract.test.ts index c86fa4817..65011ba6b 100644 --- a/test/contract.test.ts +++ b/test/contract.test.ts @@ -274,7 +274,9 @@ describe('contract', function () { }) it('should handle compiler warnings', async () => { - await expect(Project.compile()).rejects.toThrow(/Compilation warnings\:/) + await expect(Project.compile({}, '.', 'contracts', 'artifacts', undefined, true)).rejects.toThrow( + /Compilation warnings\:/ + ) const project0 = await Project.compile({ errorOnWarnings: false, ignoreUnusedConstantsWarnings: true }) expect(project0.projectArtifact.infos.get('Warnings')!.warnings).toEqual([ From 52abd3418ae346c9eb3a40b5f15d921ce9f9a0ea Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 30 May 2024 09:15:41 +0800 Subject: [PATCH 3/5] Fix get changed sources --- packages/cli/src/project.ts | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/packages/cli/src/project.ts b/packages/cli/src/project.ts index 4b81fa71e..aab2b0996 100644 --- a/packages/cli/src/project.ts +++ b/packages/cli/src/project.ts @@ -206,14 +206,21 @@ export class ProjectArtifact { return fsPromises.writeFile(filepath, content) } - getChangedSources(sourceInfos: SourceInfo[]): SourceInfo[] { - const result: SourceInfo[] = [] - for (const sourceInfo of sourceInfos) { + getChangedSources(sourceInfos: SourceInfo[]): string[] { + const result: string[] = [] + // get all changed and new sources + sourceInfos.forEach((sourceInfo) => { const info = this.infos.get(sourceInfo.name) - if (typeof info === 'undefined' || info.sourceCodeHash !== sourceInfo.sourceCodeHash) { - result.push(sourceInfo) + if (info === undefined || info.sourceCodeHash !== sourceInfo.sourceCodeHash) { + result.push(sourceInfo.name) } - } + }) + // get all removed sources + this.infos.forEach((_, name) => { + if (sourceInfos.find((s) => s.name === name) === undefined) { + result.push(name) + } + }) return result } @@ -364,7 +371,7 @@ export class Project { static checkCompilerWarnings( contracts: Map>, scripts: Map>, - changedSources: SourceInfo[], + changedSources: string[], forceRecompile: boolean, errorOnWarnings: boolean ): void { @@ -425,14 +432,14 @@ export class Project { return fsPromises.writeFile(filePath, JSON.stringify(structs, null, 2)) } - private static needToUpdate(forceRecompile: boolean, changedSources: SourceInfo[], name: string): boolean { - return forceRecompile || changedSources.find((s) => s.name === name) !== undefined + private static needToUpdate(forceRecompile: boolean, changedSources: string[], name: string): boolean { + return forceRecompile || changedSources.includes(name) } private async saveArtifactsToFile( projectRootDir: string, forceRecompile: boolean, - changedSources: SourceInfo[] + changedSources: string[] ): Promise { const artifactsRootDir = this.artifactsRootDir const saveToFile = async function (compiled: Compiled): Promise { @@ -457,13 +464,13 @@ export class Project { await this.saveProjectArtifact(projectRootDir, forceRecompile, changedSources) } - private async saveProjectArtifact(projectRootDir: string, forceRecompile: boolean, changedSources: SourceInfo[]) { + private async saveProjectArtifact(projectRootDir: string, forceRecompile: boolean, changedSources: string[]) { if (!forceRecompile) { // we should not update the `codeHashDebug` if the `forceRecompile` is disable const prevProjectArtifact = await ProjectArtifact.from(projectRootDir) if (prevProjectArtifact !== undefined) { for (const [name, info] of this.projectArtifact.infos) { - if (changedSources.find((s) => s.name === name) === undefined) { + if (!changedSources.includes(name)) { const prevInfo = prevProjectArtifact.infos.get(name) info.bytecodeDebugPatch = prevInfo?.bytecodeDebugPatch ?? info.bytecodeDebugPatch info.codeHashDebug = prevInfo?.codeHashDebug ?? info.codeHashDebug @@ -525,7 +532,7 @@ export class Project { artifactsRootDir: string, errorOnWarnings: boolean, compilerOptions: node.CompilerOptions, - changedSources: SourceInfo[], + changedSources: string[], forceRecompile: boolean ): Promise { const removeDuplicates = sourceInfos.reduce((acc: SourceInfo[], sourceInfo: SourceInfo) => { @@ -590,7 +597,7 @@ export class Project { artifactsRootDir: string, errorOnWarnings: boolean, compilerOptions: node.CompilerOptions, - changedSources: SourceInfo[], + changedSources: string[], forceRecompile: boolean ): Promise { const projectArtifact = await ProjectArtifact.from(projectRootDir) @@ -767,7 +774,7 @@ export class Project { const sourceFiles = await Project.loadSourceFiles(projectRootDir, contractsRootDir) const { errorOnWarnings, ...nodeCompilerOptions } = { ...DEFAULT_COMPILER_OPTIONS, ...compilerOptionsPartial } const projectArtifact = await ProjectArtifact.from(projectRootDir) - const changedSources = projectArtifact?.getChangedSources(sourceFiles) ?? sourceFiles + const changedSources = projectArtifact?.getChangedSources(sourceFiles) ?? sourceFiles.map((s) => s.name) if ( projectArtifact === undefined || projectArtifact.needToReCompile(nodeCompilerOptions, fullNodeVersion) || From 8a4d8cbb3d775b4f530226b22b326b400e4d2f3b Mon Sep 17 00:00:00 2001 From: lbqds Date: Thu, 30 May 2024 09:26:38 +0800 Subject: [PATCH 4/5] Add more tests --- packages/cli/src/project.test.ts | 65 ++++++++++++++++++++++++++++++++ packages/cli/src/project.ts | 4 +- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 packages/cli/src/project.test.ts diff --git a/packages/cli/src/project.test.ts b/packages/cli/src/project.test.ts new file mode 100644 index 000000000..ac31bf7a0 --- /dev/null +++ b/packages/cli/src/project.test.ts @@ -0,0 +1,65 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ + +import { DEFAULT_NODE_COMPILER_OPTIONS } from '@alephium/web3' +import { CodeInfo, ProjectArtifact, SourceInfo } from './project' + +describe('project', () => { + function newCodeInfo(hash: string): CodeInfo { + return { + sourceFile: '', + sourceCodeHash: hash, + bytecodeDebugPatch: '', + codeHashDebug: '', + warnings: [] + } + } + + function newSourceInfo(name: string, hash: string): SourceInfo { + return new SourceInfo(0, name, '', hash, '', false) + } + + it('should get changed sources', () => { + const codes: Map = new Map([ + ['Foo', newCodeInfo('Foo')], + ['Bar', newCodeInfo('Bar')], + ['Baz', newCodeInfo('Baz')] + ]) + const preArtifacts = new ProjectArtifact('', DEFAULT_NODE_COMPILER_OPTIONS, codes) + const sources = [newSourceInfo('Foo', 'Foo1'), newSourceInfo('Bar', 'Bar1'), newSourceInfo('Baz', 'Baz')] + expect(preArtifacts.getChangedSources(sources)).toEqual(['Foo', 'Bar']) + }) + + it('should get new added sources', () => { + const codes: Map = new Map([['Foo', newCodeInfo('Foo')]]) + const preArtifacts = new ProjectArtifact('', DEFAULT_NODE_COMPILER_OPTIONS, codes) + const sources = [newSourceInfo('Foo', 'Foo'), newSourceInfo('Bar', 'Bar'), newSourceInfo('Baz', 'Baz')] + expect(preArtifacts.getChangedSources(sources)).toEqual(['Bar', 'Baz']) + }) + + it('should get removed sources', () => { + const codes: Map = new Map([ + ['Foo', newCodeInfo('Foo')], + ['Bar', newCodeInfo('Bar')], + ['Baz', newCodeInfo('Baz')] + ]) + const preArtifacts = new ProjectArtifact('', DEFAULT_NODE_COMPILER_OPTIONS, codes) + const sources = [newSourceInfo('Bar', 'Bar')] + expect(preArtifacts.getChangedSources(sources)).toEqual(['Foo', 'Baz']) + }) +}) diff --git a/packages/cli/src/project.ts b/packages/cli/src/project.ts index aab2b0996..7d0c9a049 100644 --- a/packages/cli/src/project.ts +++ b/packages/cli/src/project.ts @@ -66,7 +66,7 @@ enum SourceKind { Struct = 4 } -class SourceInfo { +export class SourceInfo { type: SourceKind name: string contractRelativePath: string @@ -127,7 +127,7 @@ class Compiled { } } -type CodeInfo = { +export type CodeInfo = { sourceFile: string sourceCodeHash: string bytecodeDebugPatch: string From 19de4435b4400a864847f1c9d5e15f03bb67cc7e Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 29 May 2024 20:44:38 +0800 Subject: [PATCH 5/5] Rename --- packages/cli/src/types.ts | 3 +-- packages/cli/src/utils.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/types.ts b/packages/cli/src/types.ts index 22ce58cfa..f30a85329 100644 --- a/packages/cli/src/types.ts +++ b/packages/cli/src/types.ts @@ -33,14 +33,13 @@ import { ContractInstance } from '@alephium/web3' import { getConfigFile, loadConfig } from './utils' -import path from 'path' import { Project } from './project' export interface Network { networkId?: number nodeUrl: string privateKeys: string[] | string - deploymentStatusFile?: string + deploymentFile?: string confirmations?: number settings: Settings } diff --git a/packages/cli/src/utils.ts b/packages/cli/src/utils.ts index 17f232ffa..3adc7e38d 100644 --- a/packages/cli/src/utils.ts +++ b/packages/cli/src/utils.ts @@ -102,8 +102,8 @@ export async function isDevnetLive(): Promise { export function getDeploymentFilePath(configuration: Configuration, networkId: NetworkId): string { const network = getNetwork(configuration, networkId) - return network.deploymentStatusFile - ? network.deploymentStatusFile + return network.deploymentFile + ? network.deploymentFile : path.join(configuration.artifactDir ?? DEFAULT_CONFIGURATION_VALUES.artifactDir, `.deployments.${networkId}.json`) }