logos-delivery/tools/install_from_lock.nim
2026-07-14 08:35:37 -04:00

176 lines
5.4 KiB
Nim

## Install nimble.lock packages into nimbledeps/pkgs2 and write nimble.paths.
##
## Used when `nimble setup --localdeps` fails (e.g. SAT cannot resolve libp2p
## git pins). Reproducible: clones exact vcsRevision from the lockfile.
##
## Usage (from repo root):
## nim c -r --hints:off tools/install_from_lock.nim
##
## Optional env:
## INSTALL_FROM_LOCK_FORCE=1 re-clone packages even if present
import std/[json, os, osproc, strutils, algorithm]
const
PkgsRel = "nimbledeps" / "pkgs2"
LockRel = "nimble.lock"
PathsRel = "nimble.paths"
proc run(cmd: string): int =
execCmd(cmd)
proc runOut(cmd: string): tuple[output: string, exitCode: int] =
execCmdEx(cmd)
proc gitClone(url, dest, rev: string): bool =
if dirExists(dest):
removeDir(dest)
# Shallow clone of a specific commit is awkward; full clone then checkout.
var c = run("git clone --quiet " & quoteShell(url) & " " & quoteShell(dest))
if c != 0:
let url2 = url.replace(".git", "")
c = run("git clone --quiet " & quoteShell(url2) & " " & quoteShell(dest))
if c != 0:
return false
if run("git -C " & quoteShell(dest) & " checkout -q " & quoteShell(rev)) != 0:
discard run("git -C " & quoteShell(dest) & " fetch --quiet origin " & quoteShell(rev))
if run("git -C " & quoteShell(dest) & " checkout -q " & quoteShell(rev)) != 0:
return false
# Submodules (bearssl, boringssl, secp256k1, lsquic, zlib, ...)
discard run(
"git -C " & quoteShell(dest) & " submodule update --init --recursive --quiet"
)
true
proc packageVersion(pkgDir: string, fallback: string): string =
result = fallback
for f in walkFiles(pkgDir / "*.nimble"):
for line in readFile(f).splitLines:
let t = line.strip
if t.startsWith("version"):
let parts = t.split('=')
if parts.len >= 2:
return parts[1].strip().strip(chars = {'"', '\''})
break
proc writeMeta(pkgDir, url, rev, sha1, ver: string) =
let meta = %*{
"url": url,
"vcsRevision": rev,
"downloadMethod": "git",
"versions": [ver],
"checksums": {"sha1": sha1}
}
writeFile(pkgDir / "nimblemeta.json", meta.pretty)
proc installPkg(root, name: string, entry: JsonNode, force: bool): string =
## Returns final package directory path.
let version = entry["version"].getStr
let rev = entry["vcsRevision"].getStr
let url = entry["url"].getStr
let sha1 = entry["checksums"]["sha1"].getStr
let pkgsDir = root / PkgsRel
createDir(pkgsDir)
# Prefer existing dir matching name-*-sha1
var existing = ""
for kind, path in walkDir(pkgsDir):
if kind != pcDir:
continue
let base = path.extractFilename
if base.startsWith(name & "-") and base.endsWith("-" & sha1):
existing = path
break
if existing.len > 0 and not force and fileExists(existing / "nimblemeta.json"):
# Ensure submodules present (best-effort)
if dirExists(existing / ".git"):
discard run(
"git -C " & quoteShell(existing) &
" submodule update --init --recursive --quiet"
)
echo " skip ", existing.extractFilename
return existing
echo " install ", name, " @ ", version, " (", rev[0 .. min(11, rev.high)], ")"
let tmpName = name & "-tmp-" & sha1
let tmpDest = pkgsDir / tmpName
if not gitClone(url, tmpDest, rev):
echo " FAILED ", name
quit(1)
var pkgVer = packageVersion(tmpDest, version.replace("#", ""))
if pkgVer.len == 0:
pkgVer = version.replace("#", "")
let finalName = name & "-" & pkgVer & "-" & sha1
let finalDest = pkgsDir / finalName
if finalDest != tmpDest:
if dirExists(finalDest):
removeDir(finalDest)
moveDir(tmpDest, finalDest)
writeMeta(finalDest, url, rev, sha1, pkgVer)
echo " -> ", finalName
finalDest
proc writePaths(root: string, dirs: seq[string]) =
var lines = @["--noNimblePath"]
var sorted = dirs
sorted.sort()
for d in sorted:
lines.add("--path:\"" & d & "\"")
if dirExists(d / "src"):
lines.add("--path:\"" & d / "src" & "\"")
writeFile(root / PathsRel, lines.join("\n") & "\n")
echo "Wrote ", root / PathsRel, " (", sorted.len, " packages)"
proc applyLibp2pShims(root: string) =
## libp2p_mix still imports removed libp2p modules; restore as thin shims.
var lp = ""
let pkgs = root / PkgsRel
for kind, path in walkDir(pkgs):
if kind != pcDir:
continue
let base = path.extractFilename
if base.startsWith("libp2p-2.2") and dirExists(path / "libp2p"):
lp = path
break
if lp.len == 0:
echo " (no libp2p 2.2 package; skip shims)"
return
writeFile(
lp / "libp2p" / "utility.nim",
"""
# Shim: libp2p/utility removed in nim-libp2p 2.x; needed by current libp2p_mix pin.
import ./utils/[opt, shortlog, collections]
export opt, shortlog, collections
""",
)
createDir(lp / "libp2p" / "utils")
writeFile(
lp / "libp2p" / "utils" / "sequninit.nim",
"""
# Shim: libp2p/utils/sequninit removed; newSeqUninit lives in system.
export system
""",
)
echo " applied libp2p 2.2 shims in ", lp.extractFilename
proc main() =
let root = getCurrentDir()
let lockPath = root / LockRel
if not fileExists(lockPath):
echo "error: ", lockPath, " not found (run from repo root)"
quit(1)
let force = getEnv("INSTALL_FROM_LOCK_FORCE") == "1"
let lock = parseFile(lockPath)
echo "Installing packages from ", LockRel, " ..."
var dirs: seq[string]
for name, entry in lock["packages"].pairs:
dirs.add(installPkg(root, name, entry, force))
writePaths(root, dirs)
applyLibp2pShims(root)
echo "Done."
main()