diff --git a/src/nimble.nim b/src/nimble.nim index 67d0674b..c5a66f33 100644 --- a/src/nimble.nim +++ b/src/nimble.nim @@ -1349,7 +1349,7 @@ proc validateDevelopDependenciesVersionRanges(dependentPkg: PackageInfo, # and no special versions, so validate the ordinary version directly. The # `withinRange(PackageInfo, ...)` overload checks `metaData.specialVersions`, # which the declarative parser does not populate for develop deps (it stays - # empty), making every concrete range fail. See develop_wf_issues.md. + # empty), making every concrete range fail. if not withinRange(depPkg.basicInfo.version, dep.ver): errors.add notInRequiredRangeMsg(pkg, depPkg, dep.ver) if errors.len > 0: @@ -1452,7 +1452,7 @@ proc alignDevelopCheckouts(rootPkg: PackageInfo, options: Options, ## When the resolved solution needs a develop dependency at a version ## different from its current checkout, git-checkout the vendor repo to the ## resolved version ("align"). Stops on the first problem (dirty working copy - ## or failed checkout); never touches a dirty repo (doCheckout is --force). + ## or failed checkout); never touches a dirty repo var resolved = initTable[string, Version]() for sp in options.satResult.solvedPkgs: resolved[sp.pkgName.toLowerAscii] = sp.version @@ -1486,6 +1486,16 @@ proc lock(options: var Options, nimBin: Option[string]) = currentLockFile = options.lockFile(currentDir) lockExists = displayLockOperationStart(currentLockFile) + # For no-arg `nimble upgrade`, preserve the lock file's nim entry verbatim so + # upgrading the libraries never moves the compiler. Captured here, BEFORE the + # lock is rewritten below; written back in the dependency loop. + var prevNimDep = none(LockFileDep) + if options.action.typ == actionUpgrade and options.action.packages.len == 0 and lockExists: + for name, dep in currentLockFile.getLockedDependencies.lockedDepsFor(options): + if name.isNim: + prevNimDep = some(dep) + break + var baseDeps = options.satResult.pkgs.toSeq if options.useSystemNim: @@ -1517,9 +1527,13 @@ proc lock(options: var Options, nimBin: Option[string]) = for solvedPkg in options.satResult.solvedPkgs: if solvedPkg.pkgName.isNim and not shouldAddNim: continue if solvedPkg.pkgName == rootPkgName: continue - + + if solvedPkg.pkgName.isNim and prevNimDep.isSome: + lockDeps[noTask][solvedPkg.pkgName] = prevNimDep.get + continue + # Get the PackageInfo for this solved package - let pkgInfo = + let pkgInfo = if solvedPkg.pkgName.isFileURL: getPkgInfo(solvedPkg.pkgName.extractFilePathFromURL(), options, nimBin, pikRequires) else: diff --git a/src/nimblepkg/install.nim b/src/nimblepkg/install.nim index 68276eba..f8ea5898 100644 --- a/src/nimblepkg/install.nim +++ b/src/nimblepkg/install.nim @@ -25,6 +25,11 @@ proc getPkgInfoFromSolved*(satResult: SATResult, solvedPkg: SolvedPackage, optio #For the pkg list we need to check the version as there may be multiple versions of the same package if nameMatches(pkg, solvedPkg.pkgName, options) and pkg.basicInfo.version == solvedPkg.version: return pkg + if solvedPkg.pkgName.isNim: + result = initPackageInfo() + result.basicInfo.name = solvedPkg.pkgName + result.basicInfo.version = solvedPkg.version + return writeStackTrace() raise newNimbleError[NimbleError]("Package not found in solution: " & $solvedPkg.pkgName & " " & $solvedPkg.version) diff --git a/src/nimblepkg/nimblesat.nim b/src/nimblepkg/nimblesat.nim index 71e99dea..01069d65 100644 --- a/src/nimblepkg/nimblesat.nim +++ b/src/nimblepkg/nimblesat.nim @@ -1248,6 +1248,14 @@ proc solveLockFileDeps*(satResult: var SATResult, pkgList: seq[PackageInfo], opt shouldSolve = true break + # No-arg `nimble upgrade` = upgrade-all: re-solve the whole graph to the newest + # compatible versions. The incremental actionUpgrade branch below only bumps the + # named packages and keeps the rest locked, which is the wrong model here, so force + # the full fresh solve. (The install-preferring local solve in solvePackages is + # already skipped for actionUpgrade, so this resolves to newest.) + if options.action.typ == actionUpgrade and options.action.packages.len == 0: + shouldSolve = true + var pkgListDecl: seq[PackageInfo] for pkg in pkgList: try: diff --git a/tests/tlockfile.nim b/tests/tlockfile.nim index 72b5ffc9..e64d5bad 100644 --- a/tests/tlockfile.nim +++ b/tests/tlockfile.nim @@ -707,6 +707,125 @@ requires "nim >= 1.5.1" check newRevision == getRevision(dep1PkgName) check res.exitCode == QuitSuccess + test "upgrade with no args does not change nim": + cleanUp() + withPkgListFile: + initNewNimblePackage(mainPkgOriginRepoPath, mainPkgRepoPath, @[dep1PkgName]) + initNewNimblePackage(dep1PkgOriginRepoPath, dep1PkgRepoPath) + + var nimBefore: string + cd mainPkgRepoPath: + check execNimbleYes("lock").exitCode == QuitSuccess + let j = defaultLockFileName.readFile.parseJson + nimBefore = + if "nim" in j{$lfjkPackages}: j{$lfjkPackages}{"nim"}{$lfjkPkgVcsRevision}.str + else: "" + + cd dep1PkgOriginRepoPath: + addAdditionalFileToTheRepo("dep1.nim", "echo 1") + check execNimbleYes("install").exitCode == QuitSuccess + cd mainPkgRepoPath: + check execNimbleYes("upgrade").exitCode == QuitSuccess + let j2 = defaultLockFileName.readFile.parseJson + let nimAfter = + if "nim" in j2{$lfjkPackages}: j2{$lfjkPackages}{"nim"}{$lfjkPkgVcsRevision}.str + else: "" + check nimAfter == nimBefore # nim unchanged + + test "upgrade with no args is a no-op when already newest": + cleanUp() + withPkgListFile: + initNewNimblePackage(mainPkgOriginRepoPath, mainPkgRepoPath, @[dep1PkgName]) + initNewNimblePackage(dep1PkgOriginRepoPath, dep1PkgRepoPath) + var before: string + cd mainPkgRepoPath: + check execNimbleYes("lock").exitCode == QuitSuccess + before = getRevision(dep1PkgName) + cd mainPkgRepoPath: + let (_, exitCode) = execNimbleYes("upgrade") # nothing newer exists + check exitCode == QuitSuccess + check getRevision(dep1PkgName) == before # lock unchanged + + test "upgrade with no args realigns a develop dependency": + cleanUp() + withPkgListFile: + initNewNimblePackage(mainPkgOriginRepoPath, mainPkgRepoPath, @[dep1PkgName]) + + cdNewDir dep1PkgOriginRepoPath: + initRepo() + discard dep1PkgOriginRepoPath.initNewNimbleFile() + addFiles(dep1PkgNimbleFileName) + commit("v0.1.0"); tryDoCmdEx("git tag v0.1.0") + writeFile(dep1PkgNimbleFileName, readFile(dep1PkgNimbleFileName).replace("0.1.0", "0.2.0")) + commit("v0.2.0"); tryDoCmdEx("git tag v0.2.0") + + clone(dep1PkgOriginRepoPath, dep1PkgRepoPath) + var oldRev, newRev: string + cd dep1PkgRepoPath: + checkout("v0.1.0"); oldRev = getRepoRevision() + newRev = tryDoCmdEx("git rev-parse v0.2.0").replace("\n", "") + check oldRev != newRev + + cd mainPkgRepoPath: + let nf = mainPkgNimbleFileName.readFile + mainPkgNimbleFileName.writeFile(nf.replace(&"\"{dep1PkgName}\"", &"\"{dep1PkgName} >= 0.2.0\"")) + writeDevelopFile(developFileName, @[], @[dep1PkgRepoPath]) + let res = execNimbleYes("upgrade") + check res.exitCode == QuitSuccess + var afterRev: string + cd dep1PkgRepoPath: afterRev = getRepoRevision() + check afterRev == newRev # vendor checkout aligned to v0.2.0 + check getRevision(dep1PkgName) == newRev + + test "upgrade with no args bumps to a newer tagged version": + cleanUp() + withPkgListFile: + initNewNimblePackage(mainPkgOriginRepoPath, mainPkgRepoPath, @[dep1PkgName]) + + # dep1 origin starts at version 0.1.0 (one tag). + cdNewDir dep1PkgOriginRepoPath: + initRepo() + discard dep1PkgOriginRepoPath.initNewNimbleFile() # version 0.1.0 + addFiles(dep1PkgNimbleFileName) + commit("v0.1.0"); tryDoCmdEx("git tag v0.1.0") + clone(dep1PkgOriginRepoPath, dep1PkgRepoPath) + + cd mainPkgRepoPath: + check execNimbleYes("lock").exitCode == QuitSuccess # locks dep1 0.1.0 + + # dep1 0.2.0 appears upstream and is installed (cache populated). + var newRev: string + cd dep1PkgOriginRepoPath: + writeFile(dep1PkgNimbleFileName, + readFile(dep1PkgNimbleFileName).replace("0.1.0", "0.2.0")) + commit("v0.2.0"); tryDoCmdEx("git tag v0.2.0") + newRev = getRepoRevision() # the v0.2.0 commit + check execNimbleYes("install").exitCode == QuitSuccess + cd mainPkgRepoPath: + let (_, exitCode) = execNimbleYes("upgrade") + check exitCode == QuitSuccess + check getRevision(dep1PkgName) == newRev # lock bumped to dep1 0.2.0 + + test "upgrade with no args does not add nim to a lock that has none": + cleanUp() + withPkgListFile: + initNewNimblePackage(mainPkgOriginRepoPath, mainPkgRepoPath, @[dep1PkgName]) + initNewNimblePackage(dep1PkgOriginRepoPath, dep1PkgRepoPath) + + cd mainPkgRepoPath: + # Drop the `requires "nim >= 1.5.1"` line so nim is never locked. + let nf = mainPkgNimbleFileName.readFile + mainPkgNimbleFileName.writeFile(nf.replace("requires \"nim >= 1.5.1\"", "")) + check execNimbleYes("lock").exitCode == QuitSuccess + check "nim" notin defaultLockFileName.readFile.parseJson{$lfjkPackages} + + cd dep1PkgOriginRepoPath: + addAdditionalFileToTheRepo("dep1.nim", "echo 1") + check execNimbleYes("install").exitCode == QuitSuccess + cd mainPkgRepoPath: + check execNimbleYes("upgrade").exitCode == QuitSuccess + check "nim" notin defaultLockFileName.readFile.parseJson{$lfjkPackages} # still no nim + test "can upgrade: the new version of the package has a new dep": cleanUp() withPkgListFile: