diff --git a/backend/main/PackagePublisher.mo b/backend/main/PackagePublisher.mo index 35ab1c8e..76a15f75 100644 --- a/backend/main/PackagePublisher.mo +++ b/backend/main/PackagePublisher.mo @@ -20,7 +20,7 @@ import PackageUtils "./utils/package-utils"; module { type PackageVersion = Types.PackageVersion; - type PackageConfigV2 = Types.PackageConfigV2; + type PackageConfigV3 = Types.PackageConfigV3; type PackageFileStats = Types.PackageFileStats; type TestStats = Types.TestStats; type FileId = Types.FileId; @@ -31,7 +31,7 @@ module { type PublishingPackage = { time : Time.Time; user : Principal; - config : PackageConfigV2; + config : PackageConfigV3; storage : Principal; }; type PublishingFile = { @@ -51,7 +51,7 @@ module { let publishingFileHashers = TrieMap.TrieMap(Text.equal, Text.hash); let publishingBenchmarks = TrieMap.TrieMap(Text.equal, Text.hash); - public func startPublish(caller : Principal, config : PackageConfigV2) : async Result.Result { + public func startPublish(caller : Principal, config : PackageConfigV3) : async Result.Result { if (Principal.isAnonymous(caller)) { return #err("Unauthorized"); }; diff --git a/backend/main/main-canister.mo b/backend/main/main-canister.mo index 66551ea0..02789bba 100644 --- a/backend/main/main-canister.mo +++ b/backend/main/main-canister.mo @@ -9,6 +9,7 @@ import Result "mo:base/Result"; import Debug "mo:base/Debug"; import Principal "mo:base/Principal"; import Order "mo:base/Order"; +import Option "mo:base/Option"; import Blob "mo:base/Blob"; import IC "mo:ic"; @@ -43,6 +44,7 @@ actor class Main() { public type FileId = Types.FileId; public type Err = Text.Text; public type PackageConfigV2 = Types.PackageConfigV2; + public type PackageConfigV3 = Types.PackageConfigV3; public type PackagePublication = Types.PackagePublication; public type PackageDetails = Types.PackageDetails; public type PackageSummary = Types.PackageSummary; @@ -60,9 +62,11 @@ actor class Main() { var packageVersions = TrieMap.TrieMap(Text.equal, Text.hash); var packageOwners = TrieMap.TrieMap(Text.equal, Text.hash); - var highestConfigs = TrieMap.TrieMap(Text.equal, Text.hash); + var highestConfigsV2 = TrieMap.TrieMap(Text.equal, Text.hash); + var highestConfigs = TrieMap.TrieMap(Text.equal, Text.hash); - var packageConfigs = TrieMap.TrieMap(Text.equal, Text.hash); + var packageConfigsV2 = TrieMap.TrieMap(Text.equal, Text.hash); + var packageConfigs = TrieMap.TrieMap(Text.equal, Text.hash); var packagePublications = TrieMap.TrieMap(Text.equal, Text.hash); var fileIdsByPackage = TrieMap.TrieMap(Text.equal, Text.hash); var hashByFileId = TrieMap.TrieMap(Text.equal, Text.hash); @@ -128,14 +132,19 @@ actor class Main() { context = Blob.fromArray([]); }; - func _verifyPackageRepo(config : PackageConfigV2) : async Result.Result<(), Err> { + func _verifyPackageRepo(config : PackageConfigV3) : async Result.Result<(), Err> { await verifyPackageRepository(config.name, config.repository, transform); }; // PUBLIC // Publication - public shared ({caller}) func startPublish(config : PackageConfigV2) : async Result.Result { + public shared ({caller}) func startPublish(configPub : Types.PackageConfigV3_Publishing) : async Result.Result { + let config : PackageConfigV3 = { + configPub with + requirements = Option.get(configPub.requirements, []); + }; + let pubRes = await packagePublisher.startPublish(caller, config); if (Result.isErr(pubRes)) { return pubRes; @@ -513,12 +522,12 @@ actor class Main() { let backupManager = Backup.BackupManager(backupStateV2, {maxBackups = 20}); type BackupChunk = { - #v6 : { + #v7 : { #packagePublications : [(PackageId, PackagePublication)]; #packageVersions : [(PackageName, [PackageVersion])]; #packageOwners : [(PackageName, Principal)]; - #packageConfigs : [(PackageId, PackageConfigV2)]; - #highestConfigs : [(PackageName, PackageConfigV2)]; + #packageConfigs : [(PackageId, PackageConfigV3)]; + #highestConfigs : [(PackageName, PackageConfigV3)]; #fileIdsByPackage : [(PackageId, [FileId])]; #hashByFileId : [(FileId, Blob)]; #packageFileStats : [(PackageId, PackageFileStats)]; @@ -542,22 +551,22 @@ actor class Main() { }; func _backup() : async () { - let backup = backupManager.NewBackup("v6"); + let backup = backupManager.NewBackup("v7"); await backup.startBackup(); - await backup.uploadChunk(to_candid(#v6(#packagePublications(Iter.toArray(packagePublications.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#packageVersions(Iter.toArray(packageVersions.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#packageOwners(Iter.toArray(packageOwners.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#fileIdsByPackage(Iter.toArray(fileIdsByPackage.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#hashByFileId(Iter.toArray(hashByFileId.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#packageFileStats(Iter.toArray(packageFileStats.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#packageTestStats(Iter.toArray(packageTestStats.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#packageBenchmarks(Iter.toArray(packageBenchmarks.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#packageNotes(Iter.toArray(packageNotes.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#downloadLog(downloadLog.toStable())) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#storageManager(storageManager.toStable())) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#users(users.toStable())) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#highestConfigs(Iter.toArray(highestConfigs.entries()))) : BackupChunk)); - await backup.uploadChunk(to_candid(#v6(#packageConfigs(Iter.toArray(packageConfigs.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packagePublications(Iter.toArray(packagePublications.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packageVersions(Iter.toArray(packageVersions.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packageOwners(Iter.toArray(packageOwners.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#fileIdsByPackage(Iter.toArray(fileIdsByPackage.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#hashByFileId(Iter.toArray(hashByFileId.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packageFileStats(Iter.toArray(packageFileStats.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packageTestStats(Iter.toArray(packageTestStats.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packageBenchmarks(Iter.toArray(packageBenchmarks.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packageNotes(Iter.toArray(packageNotes.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#downloadLog(downloadLog.toStable())) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#storageManager(storageManager.toStable())) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#users(users.toStable())) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#highestConfigs(Iter.toArray(highestConfigs.entries()))) : BackupChunk)); + await backup.uploadChunk(to_candid(#v7(#packageConfigs(Iter.toArray(packageConfigs.entries()))) : BackupChunk)); await backup.finishBackup(); }; @@ -567,7 +576,7 @@ actor class Main() { assert(Utils.isAdmin(caller)); await backupManager.restore(backupId, func(blob : Blob) { - let ?#v6(chunk) : ?BackupChunk = from_candid(blob) else Debug.trap("Failed to restore chunk"); + let ?#v7(chunk) : ?BackupChunk = from_candid(blob) else Debug.trap("Failed to restore chunk"); switch (chunk) { case (#packagePublications(packagePublicationsStable)) { @@ -609,10 +618,10 @@ actor class Main() { users.loadStable(usersStable); }; case (#highestConfigs(highestConfigsStable)) { - highestConfigs := TrieMap.fromEntries(highestConfigsStable.vals(), Text.equal, Text.hash); + highestConfigs := TrieMap.fromEntries(highestConfigsStable.vals(), Text.equal, Text.hash); }; case (#packageConfigs(packageConfigsStable)) { - packageConfigs := TrieMap.fromEntries(packageConfigsStable.vals(), Text.equal, Text.hash); + packageConfigs := TrieMap.fromEntries(packageConfigsStable.vals(), Text.equal, Text.hash); }; }; }); @@ -638,9 +647,14 @@ actor class Main() { stable var packagePublicationsStable : [(PackageId, PackagePublication)] = []; stable var packageVersionsStable : [(PackageName, [PackageVersion])] = []; stable var packageOwnersStable : [(PackageName, Principal)] = []; + + // todo: remove stable var packageConfigsStableV2 : [(PackageId, PackageConfigV2)] = []; stable var highestConfigsStableV2 : [(PackageName, PackageConfigV2)] = []; + stable var packageConfigsStableV3 : [(PackageId, PackageConfigV3)] = []; + stable var highestConfigsStableV3 : [(PackageName, PackageConfigV3)] = []; + stable var fileIdsByPackageStable : [(PackageId, [FileId])] = []; stable var hashByFileIdStable : [(FileId, Blob)] = []; stable var packageFileStatsStable : [(PackageId, PackageFileStats)] = []; @@ -666,17 +680,47 @@ actor class Main() { storageManagerStable := storageManager.toStable(); usersStable := users.toStable(); - highestConfigsStableV2 := Iter.toArray(highestConfigs.entries()); - packageConfigsStableV2 := Iter.toArray(packageConfigs.entries()); + // todo: remove + highestConfigsStableV2 := Iter.toArray(highestConfigsV2.entries()); + packageConfigsStableV2 := Iter.toArray(packageConfigsV2.entries()); + + highestConfigsStableV3 := Iter.toArray(highestConfigs.entries()); + packageConfigsStableV3 := Iter.toArray(packageConfigs.entries()); }; system func postupgrade() { - packageConfigs := TrieMap.fromEntries(packageConfigsStableV2.vals(), Text.equal, Text.hash); + // todo: remove + packageConfigsV2 := TrieMap.fromEntries(packageConfigsStableV2.vals(), Text.equal, Text.hash); packageConfigsStableV2 := []; - highestConfigs := TrieMap.fromEntries(highestConfigsStableV2.vals(), Text.equal, Text.hash); + // todo: remove + highestConfigsV2 := TrieMap.fromEntries(highestConfigsStableV2.vals(), Text.equal, Text.hash); highestConfigsStableV2 := []; + // todo: remove + if (packageConfigsStableV3.size() == 0) { + func config2to3(configV2 : PackageConfigV2) : PackageConfigV3 { + { + configV2 with + requirements = []; + }; + }; + // migrate packageConfigs v2 -> v3 + packageConfigsStableV3 := Iter.map<(PackageId, PackageConfigV2), (PackageId, PackageConfigV3)>(packageConfigsV2.entries(), func((packageId, configV2)) { + (packageId, config2to3(configV2)); + }) |> Iter.toArray(_); + // migrate highestConfigs v2 -> v3 + highestConfigsStableV3 := Iter.map<(PackageId, PackageConfigV2), (PackageId, PackageConfigV3)>(highestConfigsV2.entries(), func((name, configV2)) { + (name, config2to3(configV2)); + })|> Iter.toArray(_); + }; + + packageConfigs := TrieMap.fromEntries(packageConfigsStableV3.vals(), Text.equal, Text.hash); + packageConfigsStableV3 := []; + + highestConfigs := TrieMap.fromEntries(highestConfigsStableV3.vals(), Text.equal, Text.hash); + highestConfigsStableV3 := []; + packagePublications := TrieMap.fromEntries(packagePublicationsStable.vals(), Text.equal, Text.hash); packagePublicationsStable := []; diff --git a/backend/main/registry/Registry.mo b/backend/main/registry/Registry.mo index 695dd3f7..22009a51 100644 --- a/backend/main/registry/Registry.mo +++ b/backend/main/registry/Registry.mo @@ -14,7 +14,7 @@ module { public type PackageVersion = Types.PackageVersion; public type PackageId = Types.PackageId; public type FileId = Types.FileId; - public type PackageConfigV2 = Types.PackageConfigV2; + public type PackageConfigV3 = Types.PackageConfigV3; public type PackagePublication = Types.PackagePublication; public type PackageFileStats = Types.PackageFileStats; public type TestStats = Types.TestStats; @@ -22,7 +22,7 @@ module { type NewPackageReleaseArgs = { userId : Principal; - config : PackageConfigV2; + config : PackageConfigV3; notes : Text; storageId : Principal; fileIds : [FileId]; @@ -35,8 +35,8 @@ module { public class Registry( packageVersions : TrieMap.TrieMap, packageOwners : TrieMap.TrieMap, - highestConfigs : TrieMap.TrieMap, - packageConfigs : TrieMap.TrieMap, + highestConfigs : TrieMap.TrieMap, + packageConfigs : TrieMap.TrieMap, packagePublications : TrieMap.TrieMap, fileIdsByPackage : TrieMap.TrieMap, hashByFileId : TrieMap.TrieMap, @@ -90,7 +90,7 @@ module { packageNotes.put(packageId, newRelease.notes); }; - func _updateHighestConfig(config : PackageConfigV2) { + func _updateHighestConfig(config : PackageConfigV3) { switch (getHighestVersion(config.name)) { case (?ver) { if (Semver.compare(config.version, ver) == #greater) { @@ -107,11 +107,11 @@ module { // All packages // ----------------------------- - public func getHighestConfigs() : [PackageConfigV2] { + public func getHighestConfigs() : [PackageConfigV3] { Iter.toArray(highestConfigs.vals()); }; - public func getAllConfigs() : [PackageConfigV2] { + public func getAllConfigs() : [PackageConfigV3] { Iter.toArray(packageConfigs.vals()); }; @@ -141,7 +141,7 @@ module { // By package name and version // ----------------------------- - public func getPackageConfig(name : PackageName, version : PackageVersion) : ?PackageConfigV2 { + public func getPackageConfig(name : PackageName, version : PackageVersion) : ?PackageConfigV3 { packageConfigs.get(name # "@" # version); }; diff --git a/backend/main/registry/getPackageChanges.mo b/backend/main/registry/getPackageChanges.mo index 5e6bce89..9ddc9337 100644 --- a/backend/main/registry/getPackageChanges.mo +++ b/backend/main/registry/getPackageChanges.mo @@ -10,7 +10,7 @@ module { public type PackageName = Types.PackageName; public type PackageVersion = Types.PackageVersion; public type DependencyV2 = Types.DependencyV2; - public type PackageConfigV2 = Types.PackageConfigV2; + public type PackageConfigV3 = Types.PackageConfigV3; public type TestStats = Types.TestStats; public type TestsChanges = Types.TestsChanges; public type PackageChanges = Types.PackageChanges; @@ -61,7 +61,7 @@ module { }; // deps changes - public func _computeDepsChangesBetween(prevPackageConfig : ?PackageConfigV2, curPackageConfig : ?PackageConfigV2) : [DepChange] { + public func _computeDepsChangesBetween(prevPackageConfig : ?PackageConfigV3, curPackageConfig : ?PackageConfigV3) : [DepChange] { let oldDeps = switch (prevPackageConfig) { case (?config) config.dependencies; case (null) []; @@ -74,7 +74,7 @@ module { }; // dev deps changes - public func _computeDevDepsChangesBetween(prevPackageConfig : ?PackageConfigV2, curPackageConfig : ?PackageConfigV2) : [DepChange] { + public func _computeDevDepsChangesBetween(prevPackageConfig : ?PackageConfigV3, curPackageConfig : ?PackageConfigV3) : [DepChange] { let oldDeps = switch (prevPackageConfig) { case (?config) config.devDependencies; case (null) []; diff --git a/backend/main/registry/getPackageDetails.mo b/backend/main/registry/getPackageDetails.mo index f3d77b46..0bb7dabd 100644 --- a/backend/main/registry/getPackageDetails.mo +++ b/backend/main/registry/getPackageDetails.mo @@ -22,7 +22,7 @@ module { public type PackageDetails = Types.PackageDetails; public type PackageSummaryWithChanges = Types.PackageSummaryWithChanges; public type DependencyV2 = Types.DependencyV2; - public type PackageConfigV2 = Types.PackageConfigV2; + public type PackageConfigV3 = Types.PackageConfigV3; // package details that appear on the package page public func getPackageDetails(registry : Registry.Registry, users : Users.Users, downloadLog : DownloadLog.DownloadLog, name : PackageName, version : PackageVersion) : ?PackageDetails { @@ -62,7 +62,7 @@ module { // dependents func _getPackageDependents(name : PackageName) : [PackageSummary] { - func isDependent(config : PackageConfigV2) : Bool { + func isDependent(config : PackageConfigV3) : Bool { let dependent = Option.isSome(Array.find(config.dependencies, func(dep : DependencyV2) { dep.name == name and dep.repo == ""; })); @@ -72,17 +72,17 @@ module { dependent or devDependent; }; - let dependentConfigs = Array.filter(registry.getAllConfigs(), isDependent); + let dependentConfigs = Array.filter(registry.getAllConfigs(), isDependent); - let pkgHash = func(a : PackageConfigV2) : Hash.Hash { + let pkgHash = func(a : PackageConfigV3) : Hash.Hash { Text.hash(a.name); }; - let pkgEqual = func(a : PackageConfigV2, b : PackageConfigV2) : Bool { + let pkgEqual = func(a : PackageConfigV3, b : PackageConfigV3) : Bool { a.name == b.name; }; - let unique = TrieSet.toArray(TrieSet.fromArray(dependentConfigs, pkgHash, pkgEqual)).vals(); + let unique = TrieSet.toArray(TrieSet.fromArray(dependentConfigs, pkgHash, pkgEqual)).vals(); - let summaries = Iter.map(unique, func(config) { + let summaries = Iter.map(unique, func(config) { let ?summary = getPackageSummary(registry, users, downloadLog, config.name, config.version) else Debug.trap("Package '" # name # "' not found"); summary; }); diff --git a/backend/main/registry/searchInRegistry.mo b/backend/main/registry/searchInRegistry.mo index cacf5858..737a6b0e 100644 --- a/backend/main/registry/searchInRegistry.mo +++ b/backend/main/registry/searchInRegistry.mo @@ -14,7 +14,7 @@ import {getPackageSummary} "./getPackageSummary"; module { public type PackageSummary = Types.PackageSummary; - public type PackageConfigV2 = Types.PackageConfigV2; + public type PackageConfigV3 = Types.PackageConfigV3; public type PageCount = Nat; public func searchInRegistry(registry : Registry.Registry, users : Users.Users, downloadLog : DownloadLog.DownloadLog, searchText : Text, limitOpt : ?Nat, pageIndexOpt : ?Nat) : ([PackageSummary], PageCount) { @@ -26,7 +26,7 @@ module { assert(searchText.size() <= 100); type ConfigWithPoints = { - config : PackageConfigV2; + config : PackageConfigV3; sortingPoints : Nat; }; let matchedConfigs = Buffer.Buffer(0); diff --git a/backend/main/types.mo b/backend/main/types.mo index 8ec894ba..54df6823 100644 --- a/backend/main/types.mo +++ b/backend/main/types.mo @@ -53,29 +53,44 @@ module { version : Text; // max 20 }; + public type Requirement = { + name : Text; // moc + value : Text; // version for moc + }; + public type PackageConfigV2 = { name : PackageName; // max 50 version : Text; // max 20 description : Text; // max 200 repository : Text; // max 300 keywords : [Text]; // max 5 items, max 20 chars - documentation : Text; // max 300 - homepage : Text; // max 300 baseDir : Text; // max 50 readme : Text; // max 100 license : Text; // max 30 - donation : Text; // empty or 64 chars dependencies : [DependencyV2]; // max 100 items devDependencies : [DependencyV2]; // max 100 items + // legacy for backward compatibility + documentation : Text; // max 300 + homepage : Text; // max 300 + donation : Text; // empty or 64 chars scripts : [Script]; // max 40 items dfx : Text; // max 10 moc : Text; // max 10 }; + // legacy for backward compatibility + public type PackageConfigV3_Publishing = PackageConfigV2 and { + requirements : ?[Requirement]; // max 1 item + }; + + public type PackageConfigV3 = PackageConfigV2 and { + requirements : [Requirement]; // max 1 item + }; + public type PackageSummary = { owner : Principal; // TODO: ownerId? ownerInfo : User; - config : PackageConfigV2; + config : PackageConfigV3; publication : PackagePublication; downloadsTotal : Nat; downloadsInLast30Days : Nat; diff --git a/backend/main/utils/validateConfig.mo b/backend/main/utils/validateConfig.mo index b817cf10..cea4ed12 100644 --- a/backend/main/utils/validateConfig.mo +++ b/backend/main/utils/validateConfig.mo @@ -11,7 +11,7 @@ import {isLowerCaseLetter} "./is-letter"; import {validateLicense} "./validateLicense"; module { - type PackageConfigV2 = Types.PackageConfigV2; + type PackageConfigV3 = Types.PackageConfigV3; type DependencyV2 = Types.DependencyV2; type Err = Types.Err; @@ -59,7 +59,7 @@ module { "name", ]; - public func validateConfig(config : PackageConfigV2) : Result.Result<(), Err> { + public func validateConfig(config : PackageConfigV3) : Result.Result<(), Err> { // temporarily disabled fields if (config.dfx.size() > 0) { return #err("invalid config: 'dfx' field is not supported yet"); @@ -79,6 +79,8 @@ module { if (config.donation.size() > 0) { return #err("invalid config: 'donation' field is not supported yet"); }; + + // hardcoded fields if (config.baseDir != "src") { return #err("invalid config: 'baseDir' field must be equal to 'src'"); }; @@ -201,6 +203,18 @@ module { return depValid; }; }; + if (config.requirements.size() > 1) { + return #err("invalid config: max requirements is 1"); + }; + for (req in config.requirements.vals()) { + if (req.name != "moc") { + return #err("invalid config: unknown requirement '" # req.name # "'"); + }; + let versionValid = Semver.validate(req.value); + if (Result.isErr(versionValid)) { + return versionValid; + }; + }; #ok; }; diff --git a/cli/check-requirements.ts b/cli/check-requirements.ts new file mode 100644 index 00000000..ceacf508 --- /dev/null +++ b/cli/check-requirements.ts @@ -0,0 +1,45 @@ +import path from 'node:path'; + +import {getDependencyType, readConfig} from './mops.js'; +import {resolvePackages} from './resolve-packages.js'; +import {SemVer} from 'semver'; +import chalk from 'chalk'; + +export async function checkRequirements({verbose = false} = {}) { + let config = readConfig(); + if (!config.toolchain?.moc) { + return; + } + let installedMoc = new SemVer(config.toolchain.moc); + let highestRequiredMoc = new SemVer('0.0.0'); + let highestRequiredMocPkgId = ''; + + let resolvedPackages = await resolvePackages(); + for (let [name, version] of Object.entries(resolvedPackages)) { + if (getDependencyType(version) === 'mops') { + let pkgId = `${name}@${version}`; + let depConfig = readConfig(path.join('.mops', pkgId, 'mops.toml')); + let moc = depConfig.requirements?.moc; + + if (moc) { + let requiredMoc = new SemVer(moc); + if (highestRequiredMoc.compare(requiredMoc) < 0) { + highestRequiredMoc = requiredMoc; + highestRequiredMocPkgId = pkgId; + } + verbose && _check(pkgId, installedMoc, requiredMoc); + } + } + } + + verbose || _check(highestRequiredMocPkgId, installedMoc, highestRequiredMoc); +} + +function _check(pkgId : string, installedMoc : SemVer, requiredMoc : SemVer) { + let comp = installedMoc.compare(requiredMoc); + if (comp < 0) { + console.log(chalk.yellow(`moc version does not meet the requirements of ${pkgId}`)); + console.log(chalk.yellow(` Required: >= ${requiredMoc.format()}`)); + console.log(chalk.yellow(` Installed: ${installedMoc.format()}`)); + } +} \ No newline at end of file diff --git a/cli/commands/add.ts b/cli/commands/add.ts index 85d1d4fd..666712a6 100644 --- a/cli/commands/add.ts +++ b/cli/commands/add.ts @@ -7,6 +7,7 @@ import {installFromGithub} from '../vessel.js'; import {install} from './install.js'; import {notifyInstalls} from '../notify-installs.js'; import {checkIntegrity} from '../integrity.js'; +import {checkRequirements} from '../check-requirements.js'; type AddOptions = { verbose ?: boolean; @@ -120,5 +121,8 @@ export async function add(name : string, {verbose = false, dev = false, lock} : ]); logUpdate.clear(); + + await checkRequirements({verbose}); + console.log(chalk.green('Package installed ') + `${pkgDetails.name} = "${pkgDetails.repo || pkgDetails.path || pkgDetails.version}"`); } \ No newline at end of file diff --git a/cli/commands/install-all.ts b/cli/commands/install-all.ts index d86349e9..91d0cc4b 100644 --- a/cli/commands/install-all.ts +++ b/cli/commands/install-all.ts @@ -7,6 +7,7 @@ import {installFromGithub} from '../vessel.js'; import {notifyInstalls} from '../notify-installs.js'; import {checkIntegrity} from '../integrity.js'; import {installLocal} from './install-local.js'; +import {checkRequirements} from '../check-requirements.js'; type InstallAllOptions = { verbose ?: boolean; @@ -51,6 +52,7 @@ export async function installAll({verbose = false, silent = false, lock} : Insta if (!silent) { logUpdate.clear(); + await checkRequirements(); console.log(chalk.green('Packages installed')); } } \ No newline at end of file diff --git a/cli/commands/publish.ts b/cli/commands/publish.ts index 0872408c..1c4b37d4 100644 --- a/cli/commands/publish.ts +++ b/cli/commands/publish.ts @@ -10,7 +10,7 @@ import {checkConfigFile, getIdentity, getRootDir, progressBar, readConfig} from import {mainActor} from '../api/actors.js'; import {parallel} from '../parallel.js'; import {docs} from './docs.js'; -import {Benchmarks, DependencyV2, PackageConfigV2} from '../declarations/main/main.did.js'; +import {Benchmarks, DependencyV2, PackageConfigV3_Publishing, Requirement} from '../declarations/main/main.did.js'; import {Dependency} from '../types.js'; import {testWithReporter} from './test/test.js'; import {SilentReporter} from './test/reporters/silent-reporter.js'; @@ -29,7 +29,7 @@ export async function publish(options : {docs ?: boolean, test ?: boolean, bench // validate for (let key of Object.keys(config)) { - if (!['package', 'dependencies', 'dev-dependencies', 'toolchain'].includes(key)) { + if (!['package', 'dependencies', 'dev-dependencies', 'toolchain', 'requirements'].includes(key)) { console.log(chalk.red('Error: ') + `Unknown config section [${key}]`); process.exit(1); } @@ -160,6 +160,15 @@ export async function publish(options : {docs ?: boolean, test ?: boolean, bench } } + if (config.requirements) { + Object.keys(config.requirements).forEach((name) => { + if (name !== 'moc') { + console.log(chalk.red('Error: ') + `Unknown requirement "${name}"`); + return; + } + }); + } + let toBackendDep = (dep : Dependency) : DependencyV2 => { return { ...dep, @@ -168,8 +177,12 @@ export async function publish(options : {docs ?: boolean, test ?: boolean, bench }; }; + let toBackendReq = ([name, value] : [string, string]) : Requirement => { + return {name, value}; + }; + // map fields - let backendPkgConfig : PackageConfigV2 = { + let backendPkgConfig : PackageConfigV3_Publishing = { name: config.package.name, version: config.package.version, keywords: config.package.keywords || [], @@ -186,6 +199,7 @@ export async function publish(options : {docs ?: boolean, test ?: boolean, bench dependencies: Object.values(config.dependencies || {}).map(toBackendDep), devDependencies: Object.values(config['dev-dependencies'] || {}).map(toBackendDep), scripts: [], + requirements: [Object.entries(config.requirements || {}).map((req) => toBackendReq(req))], }; let defaultFiles = [ diff --git a/cli/commands/toolchain/index.ts b/cli/commands/toolchain/index.ts index 8b6ba013..70416124 100644 --- a/cli/commands/toolchain/index.ts +++ b/cli/commands/toolchain/index.ts @@ -7,6 +7,7 @@ import prompts from 'prompts'; import {createLogUpdate} from 'log-update'; import {checkConfigFile, getClosestConfigFile, getRootDir, globalCacheDir, readConfig, writeConfig} from '../../mops.js'; import {Tool} from '../../types.js'; +import {checkRequirements} from '../../check-requirements.js'; import * as moc from './moc.js'; import * as pocketIc from './pocket-ic.js'; import * as wasmtime from './wasmtime.js'; @@ -243,6 +244,8 @@ async function use(tool : Tool, version ?: string) { config.toolchain[tool] = version; writeConfig(config); + await checkRequirements(); + if (oldVersion === version) { console.log((`${tool} ${version} is already installed`)); } @@ -277,6 +280,10 @@ async function update(tool ?: Tool) { config.toolchain[tool] = version; writeConfig(config); + if (tool === 'moc') { + await checkRequirements(); + } + if (oldVersion === version) { console.log((`Latest ${tool} ${version} is already installed`)); } diff --git a/cli/declarations/bench/bench.did.d.ts b/cli/declarations/bench/bench.did.d.ts index 2b0fe6a2..23b4ad78 100644 --- a/cli/declarations/bench/bench.did.d.ts +++ b/cli/declarations/bench/bench.did.d.ts @@ -26,4 +26,4 @@ export interface anon_class_10_1 { } export interface _SERVICE extends anon_class_10_1 {} export declare const idlFactory: IDL.InterfaceFactory; -export declare const init: ({ IDL }: { IDL: IDL }) => IDL.Type[]; +export declare const init: (args: { IDL: typeof IDL }) => IDL.Type[]; diff --git a/cli/declarations/bench/index.js b/cli/declarations/bench/index.js index f7a29ce9..64c1599f 100644 --- a/cli/declarations/bench/index.js +++ b/cli/declarations/bench/index.js @@ -10,8 +10,7 @@ export { idlFactory } from "./bench.did.js"; * beginning in dfx 0.15.0 */ export const canisterId = - process.env.CANISTER_ID_BENCH || - process.env.BENCH_CANISTER_ID; + process.env.CANISTER_ID_BENCH; export const createActor = (canisterId, options = {}) => { const agent = options.agent || new HttpAgent({ ...options.agentOptions }); diff --git a/cli/declarations/main/index.js b/cli/declarations/main/index.js index 832654e8..e51af5e6 100644 --- a/cli/declarations/main/index.js +++ b/cli/declarations/main/index.js @@ -10,8 +10,7 @@ export { idlFactory } from "./main.did.js"; * beginning in dfx 0.15.0 */ export const canisterId = - process.env.CANISTER_ID_MAIN || - process.env.MAIN_CANISTER_ID; + process.env.CANISTER_ID_MAIN; export const createActor = (canisterId, options = {}) => { const agent = options.agent || new HttpAgent({ ...options.agentOptions }); diff --git a/cli/declarations/main/main.did b/cli/declarations/main/main.did index bc4dab45..5cb8dbf3 100644 --- a/cli/declarations/main/main.did +++ b/cli/declarations/main/main.did @@ -132,6 +132,11 @@ type Response = streaming_strategy: opt StreamingStrategy; upgrade: opt bool; }; +type Requirement = + record { + name: text; + value: text; + }; type Request = record { body: blob; @@ -145,7 +150,7 @@ type PageCount = nat; type PackageVersion = text; type PackageSummary__1 = record { - config: PackageConfigV2__1; + config: PackageConfigV3; downloadsInLast30Days: nat; downloadsInLast7Days: nat; downloadsTotal: nat; @@ -157,7 +162,7 @@ type PackageSummary__1 = type PackageSummaryWithChanges__1 = record { changes: PackageChanges; - config: PackageConfigV2__1; + config: PackageConfigV3; downloadsInLast30Days: nat; downloadsInLast7Days: nat; downloadsTotal: nat; @@ -169,7 +174,7 @@ type PackageSummaryWithChanges__1 = type PackageSummaryWithChanges = record { changes: PackageChanges; - config: PackageConfigV2__1; + config: PackageConfigV3; downloadsInLast30Days: nat; downloadsInLast7Days: nat; downloadsTotal: nat; @@ -180,7 +185,7 @@ type PackageSummaryWithChanges = }; type PackageSummary = record { - config: PackageConfigV2__1; + config: PackageConfigV3; downloadsInLast30Days: nat; downloadsInLast7Days: nat; downloadsTotal: nat; @@ -218,7 +223,7 @@ type PackageDetails = record { benchmarks: Benchmarks__1; changes: PackageChanges; - config: PackageConfigV2__1; + config: PackageConfigV3; dependents: vec PackageSummary__1; deps: vec PackageSummary__1; devDeps: vec PackageSummary__1; @@ -234,7 +239,7 @@ type PackageDetails = testStats: TestStats__1; versionHistory: vec PackageSummaryWithChanges__1; }; -type PackageConfigV2__1 = +type PackageConfigV3_Publishing = record { baseDir: text; dependencies: vec DependencyV2; @@ -250,10 +255,11 @@ type PackageConfigV2__1 = name: PackageName__1; readme: text; repository: text; + requirements: opt vec Requirement; scripts: vec Script; version: text; }; -type PackageConfigV2 = +type PackageConfigV3 = record { baseDir: text; dependencies: vec DependencyV2; @@ -269,6 +275,7 @@ type PackageConfigV2 = name: PackageName__1; readme: text; repository: text; + requirements: vec Requirement; scripts: vec Script; version: text; }; @@ -341,7 +348,7 @@ type Main = search: (Text, opt nat, opt nat) -> (vec PackageSummary, PageCount) query; setUserProp: (text, text) -> (Result_1); startFileUpload: (PublishingId, Text, nat, blob) -> (Result_3); - startPublish: (PackageConfigV2) -> (Result_2); + startPublish: (PackageConfigV3_Publishing) -> (Result_2); transferOwnership: (PackageName, principal) -> (Result_1); transformRequest: (HttpTransformArg) -> (HttpResponse) query; uploadBenchmarks: (PublishingId, Benchmarks) -> (Result); diff --git a/cli/declarations/main/main.did.d.ts b/cli/declarations/main/main.did.d.ts index 96db591a..6ca49c61 100644 --- a/cli/declarations/main/main.did.d.ts +++ b/cli/declarations/main/main.did.d.ts @@ -117,7 +117,7 @@ export interface Main { [PublishingId, Text, bigint, Uint8Array | number[]], Result_3 >, - 'startPublish' : ActorMethod<[PackageConfigV2], Result_2>, + 'startPublish' : ActorMethod<[PackageConfigV3_Publishing], Result_2>, 'transferOwnership' : ActorMethod<[PackageName, Principal], Result_1>, 'transformRequest' : ActorMethod<[HttpTransformArg], HttpResponse>, 'uploadBenchmarks' : ActorMethod<[PublishingId, Benchmarks], Result>, @@ -136,7 +136,7 @@ export interface PackageChanges { 'notes' : string, 'devDeps' : Array, } -export interface PackageConfigV2 { +export interface PackageConfigV3 { 'dfx' : string, 'moc' : string, 'scripts' : Array