diff options
Diffstat (limited to 'src/feature/syncsource.nim')
-rw-r--r-- | src/feature/syncsource.nim | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/src/feature/syncsource.nim b/src/feature/syncsource.nim new file mode 100644 index 0000000..fba7062 --- /dev/null +++ b/src/feature/syncsource.nim @@ -0,0 +1,173 @@ +import + future, options, os, posix, sequtils, strutils, tables, + "../args", "../aur", "../common", "../config", "../format", "../lists", + "../package", "../pacman", "../utils", + "../wrapper/alpm" + +type + BaseTarget = tuple[ + base: string, + version: string, + destination: string, + aurGitUrl: Option[string], + gitRepo: Option[GitRepo] + ] + + CloneResult = tuple[ + base: string, + path: string, + files: seq[string], + destination: string + ] + +proc getFilesOrClear(base: string, repoPath: string, gitSubdir: Option[string]): + (seq[string], Option[string]) = + let rawFiles = getGitFiles(repoPath, gitSubdir, false) + .filter(f => f != ".gitignore" and f != ".SRCINFO" and f.find('/') < 0) + .map(f => gitSubdir.map(s => s & "/" & f).get(f)) + + if rawFiles.len > 0: + (rawFiles, none(string)) + else: + removeDirQuiet(repoPath) + (newSeq[string](), some(tr"$#: failed to clone git repository" % [base])) + +proc cloneRepositories(config: Config, targets: seq[BaseTarget], + update: (int, int) -> void): (List[CloneResult], List[string]) = + proc cloneNext(index: int, results: List[CloneResult], messages: List[string]): + (List[CloneResult], List[string]) = + update(index, targets.len) + + if index >= targets.len: + (results.reversed, messages.reversed) + else: + let target = targets[index] + let repoPath = repoPath(config.tmpRootCurrent, target.base) + removeDirQuiet(repoPath) + + if target.aurGitUrl.isSome: + let (cloneCode, cerror) = cloneAurRepo(config, + target.base, target.aurGitUrl.unsafeGet, false) + + if cloneCode != 0: + cloneNext(index + 1, results, toSeq(cerror.items) ^& messages) + else: + let (files, ferror) = getFilesOrClear(target.base, repoPath, none(string)) + if ferror.isSome: + cloneNext(index + 1, results, ferror.unsafeGet ^& messages) + else: + cloneNext(index + 1, (target.base, repoPath, files, + target.destination) ^& results, messages) + elif target.gitRepo.isSome: + let gitRepo = target.gitRepo.unsafeGet + let cerror = clonePackageRepo(config, target.base, + target.version, gitRepo, false) + + if cerror.isSome: + cloneNext(index + 1, results, cerror.unsafeGet ^& messages) + else: + let (files, ferror) = getFilesOrClear(target.base, repoPath, some(gitRepo.path)) + if ferror.isSome: + cloneNext(index + 1, results, ferror.unsafeGet ^& messages) + else: + cloneNext(index + 1, (target.base, repoPath, files, + target.destination) ^& results, messages) + else: + let message = tr"$#: repository not found" % [target.base] + cloneNext(index + 1, results, message ^& messages) + + cloneNext(0, nil, nil) + +proc copyFiles(config: Config, quiet: bool, results: seq[CloneResult]): List[string] = + proc copyNext(index: int, messages: List[string]): List[string] = + if index >= results.len: + messages.reversed + else: + let res = results[index] + discard mkdir(res.destination, 0o755) + + let error = try: + for f in res.files: + let index = f.rfind('/') + let name = if index >= 0: f[index + 1 .. ^1] else: f + let dest = if res.destination == ".": name else: res.destination & "/" & name + moveFile(res.path & "/" & f, dest) + printFile(config.color, quiet, res.base, dest) + none(string) + except OSError: + some(tr"$#: failed to move files" % [res.base]) + + copyNext(index + 1, toSeq(error.items) ^& messages) + + copyNext(0, nil) + +proc cloneAndCopy(config: Config, quiet: bool, + fullTargets: seq[FullPackageTarget[RpcPackageInfo]]): int = + let baseTargets = fullTargets.foldl(block: + let bases = a.map(x => x.base) + if b.isAurTargetFull: + let rpcInfo = b.pkgInfo.get + if rpcInfo.base in bases: + a + else: + a & (rpcInfo.base, rpcInfo.version, b.destination.get(rpcInfo.base), + some(rpcInfo.gitUrl), none(GitRepo)) + else: + let foundInfo = b.foundInfos[0] + let pkg = foundInfo.pkg.get + if pkg.base in bases: + a + else: + let git = lookupGitRepo(foundInfo.repo, pkg.base, pkg.arch.get) + a & (pkg.base, pkg.version, b.destination.get(pkg.base), + none(string), git), + newSeq[BaseTarget]()) + + let (update, terminate) = if quiet: + (proc (a: int, b: int) {.closure.} = discard, proc () {.closure.} = discard) + else: + printProgressShare(config.progressBar, tr"cloning repositories") + + let (results, rerrors) = cloneRepositories(config, baseTargets, update) + terminate() + for e in rerrors: printError(config.color, e) + + let cerrors = copyFiles(config, quiet, toSeq(results.items)) + for e in cerrors: printError(config.color, e) + + for result in results: + removeDirQuiet(result.path) + discard rmdir(config.tmpRootCurrent) + + if rerrors != nil and cerrors != nil: + 1 + else: + 0 + +proc handleSyncSource*(args: seq[Argument], config: Config): int = + discard checkAndRefresh(config.color, args) + + let quiet = args.check(%%%"quiet") + let targets = args.packageTargets(true) + + if targets.len == 0: + printError(config.color, trp("no targets specified (use -h for help)\n")) + 1 + else: + let (syncTargets, checkAurNames) = withAlpm(config.root, config.db, + config.dbs, config.arch, handle, dbs, errors): + for e in errors: printError(config.color, e) + findSyncTargets(handle, dbs, targets, false, false) + + let (rpcInfos, aerrors) = getRpcPackageInfos(checkAurNames) + for e in aerrors: printError(config.color, e) + + let notFoundTargets = filterNotFoundSyncTargets(syncTargets, + rpcInfos, initTable[string, PackageReference]()) + + if notFoundTargets.len > 0: + printSyncNotFound(config, notFoundTargets) + 1 + else: + let fullTargets = mapAurTargets[RpcPackageInfo](syncTargets, rpcInfos) + cloneAndCopy(config, quiet, fullTargets) |