Update.vala 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. using Invercargill;
  2. using Invercargill.DataStructures;
  3. /**
  4. * `usm update [-y|--yes] [<packages>]` — bring installed packages up to
  5. * the newest version their repositories offer.
  6. *
  7. * Without package names every installed package with a newer repository
  8. * version becomes a resolution root; with names exactly those packages
  9. * do (already-current names are skipped with a note). Either way there
  10. * is one resolution, one {@link Usm.TransactionSummary}, one confirm —
  11. * and the downloads happen only once the plan is accepted.
  12. *
  13. * Dependencies already installed at the resolved version are left alone;
  14. * an installed package moving to a different version is removed
  15. * (RemoveType.UPGRADE) and the new version installed in its place. The
  16. * old version's state directory is kept, which is what makes `usm
  17. * downgrade` possible.
  18. */
  19. private int update_main(string[] args) {
  20. var assume_yes = false;
  21. var package_names = new Vector<string>();
  22. for(int i = 2; i < args.length; i++) {
  23. var argument = args[i];
  24. if(argument == "-y" || argument == "--yes") {
  25. assume_yes = true;
  26. }
  27. else if(argument.has_prefix("-")) {
  28. printerr(@"Unknown option \"$argument\"\n");
  29. return update_usage();
  30. }
  31. else {
  32. package_names.add(argument);
  33. }
  34. }
  35. var progress = new Usm.Cli.ProgressBar();
  36. Usm.SystemState state = null;
  37. try {
  38. state = new Usm.SystemState(paths);
  39. }
  40. catch(Error e) {
  41. printerr(@"This system is not managed by usm: $(e.message)\n");
  42. return 245;
  43. }
  44. try {
  45. printerr("Refreshing repositories...\n");
  46. var resolver = new Usm.Resolver(new Usm.ResourceFinder());
  47. foreach(var repo in state.get_repositories()) {
  48. state.refresh_list(repo, (f, c, t) => printerr(@"Refreshing list for $(repo.name): downloading $f $c/$t bytes\r"));
  49. var listing = state.get_latest_list(repo);
  50. if(listing != null) {
  51. resolver.load_listing(repo, listing);
  52. }
  53. printerr("\n");
  54. }
  55. // Cached packages can satisfy dependencies, including the old
  56. // versions kept behind by earlier updates
  57. resolver.load_cache(paths);
  58. var installed_by_name = new Dictionary<string, Usm.CachedPackage>();
  59. var installed_versions = new Dictionary<string, Usm.Version>();
  60. foreach(var installed in state.get_installed_packages()) {
  61. var manifest = installed.get_manifest();
  62. Usm.Version seen;
  63. if(installed_versions.try_get(manifest.name, out seen) && !manifest.version.greater_than(seen)) {
  64. continue;
  65. }
  66. installed_by_name.set(manifest.name, state.resolve_installed(installed));
  67. installed_versions.set(manifest.name, manifest.version);
  68. }
  69. var roots = new Vector<Usm.AbstractPackage>();
  70. if(package_names.length > 0) {
  71. foreach(var package_name in package_names) {
  72. Usm.CachedPackage installed;
  73. if(!installed_by_name.try_get(package_name, out installed)) {
  74. printerr(@"No installed package named \"$package_name\"\n");
  75. return 254;
  76. }
  77. var target = resolver.find_package(package_name);
  78. if(target == null) {
  79. printerr(@"No package named \"$package_name\" found in any repository or the cache\n");
  80. return 254;
  81. }
  82. Usm.Version installed_version;
  83. installed_versions.try_get(package_name, out installed_version);
  84. if(!target.manifest.version.greater_than(installed_version)) {
  85. print(@"\"$package_name\" is already up to date.\n");
  86. continue;
  87. }
  88. roots.add(target);
  89. }
  90. if(roots.length == 0) {
  91. return 0;
  92. }
  93. }
  94. else {
  95. foreach(var pair in installed_by_name) {
  96. var target = resolver.find_package(pair.key);
  97. if(target != null && target.manifest.version.greater_than(installed_versions[pair.key])) {
  98. roots.add(target);
  99. }
  100. }
  101. if(roots.length == 0) {
  102. print("All installed packages are up to date.\n");
  103. return 0;
  104. }
  105. }
  106. var spm = new Usm.SystemPackageManager(state.config);
  107. Usm.ResolutionResult resolution;
  108. try {
  109. resolution = resolver.resolve(roots, spm);
  110. }
  111. catch(Usm.ResolverError e) {
  112. printerr("%s\n".printf(e.message));
  113. return 240;
  114. }
  115. // Build the transaction set WITHOUT downloading: dependencies
  116. // already installed at the resolved version stay as-is (so an
  117. // update plan names only what actually moves), and repository
  118. // packages join as their cache directories carrying the listing
  119. // manifest — the plan is confirmed before a single byte moves
  120. var current_names = new HashSet<string>();
  121. foreach(var package in resolution.install_order) {
  122. Usm.Version installed_version;
  123. if(installed_versions.try_get(package.manifest.name, out installed_version)
  124. && installed_version.compare(package.manifest.version) == 0) {
  125. current_names.add(package.manifest.name);
  126. }
  127. }
  128. var cached_packages = new HashSet<Usm.CachedPackage>();
  129. uint download_count = 0;
  130. int64 download_bytes = 0;
  131. var download_sizes_known = true;
  132. foreach(var package in resolution.install_order) {
  133. if(current_names.contains(package.manifest.name)) {
  134. continue;
  135. }
  136. if(package.repository == null) {
  137. if(package.package_path != null) {
  138. cached_packages.add(new Usm.CachedPackage(Path.get_dirname((!)package.package_path)));
  139. }
  140. continue;
  141. }
  142. var path = state.generate_cache_path(package.manifest);
  143. cached_packages.add(new Usm.CachedPackage(path) {
  144. known_manifest = package.manifest
  145. });
  146. if(File.new_for_path(Path.build_filename(path, "package.usmc")).query_exists()) {
  147. continue;
  148. }
  149. download_count++;
  150. var size = install_package_download_size(package.repository, package.repository_entry);
  151. if(size == null) {
  152. download_sizes_known = false;
  153. }
  154. else {
  155. download_bytes += size;
  156. }
  157. }
  158. // The installed versions being replaced by a different version
  159. var to_remove = new HashSet<Usm.CachedPackage>();
  160. foreach(var package in resolution.install_order) {
  161. Usm.CachedPackage installed;
  162. Usm.Version installed_version;
  163. if(!installed_by_name.try_get(package.manifest.name, out installed)) {
  164. continue;
  165. }
  166. if(installed_versions.try_get(package.manifest.name, out installed_version)
  167. && installed_version.compare(package.manifest.version) != 0) {
  168. to_remove.add(installed);
  169. }
  170. }
  171. var install_order = new Vector<string>();
  172. foreach(var package in resolution.install_order) {
  173. install_order.add(package.manifest.name);
  174. }
  175. // The exact reverse: dependents are removed before their providers
  176. var remove_order = new Vector<string>();
  177. foreach(var package in resolution.removal_order) {
  178. remove_order.add(package.manifest.name);
  179. }
  180. var transaction = new Usm.Transaction() {
  181. paths = paths,
  182. resource_finder = new Usm.ResourceFinder(paths),
  183. to_remove = to_remove,
  184. to_install = cached_packages,
  185. install_order = install_order,
  186. remove_order = remove_order,
  187. state = state
  188. };
  189. // Planning before the prompt populates the rebuildDependants
  190. // entries so the summary shows the whole transaction
  191. try {
  192. transaction.strategise();
  193. }
  194. catch(Usm.TransactionError e) {
  195. printerr(@"$(e.message)\n");
  196. return 240;
  197. }
  198. var summary = new Usm.TransactionSummary() {
  199. to_install = cached_packages.to_vector(),
  200. to_remove = to_remove.to_vector(),
  201. to_rebuild = transaction.rebuilds,
  202. download_count = download_count,
  203. download_bytes = download_sizes_known ? download_bytes : -1
  204. };
  205. if(!Usm.Cli.confirm_transaction(summary, assume_yes)) {
  206. print("Aborted.\n");
  207. return 1;
  208. }
  209. // Only now that the plan is confirmed do the packages transfer
  210. install_download_packages(state, resolution, current_names);
  211. if(resolution.system_packages.any()) {
  212. if(!install_system_packages(spm, resolution.system_packages)) {
  213. return 239;
  214. }
  215. }
  216. printerr("\nRunning transaction...\n");
  217. transaction.progress_updated.connect(progress.on_transaction_progress);
  218. transaction.run();
  219. progress.finish();
  220. }
  221. catch(Error e) {
  222. progress.fail();
  223. printerr(@"Error: $(e.message)\n");
  224. return 238;
  225. }
  226. return 0;
  227. }
  228. private int update_usage() {
  229. printerr("USAGE:\n\tusm update [-y|--yes] [<packages>]\n");
  230. return 255;
  231. }