1 /* 2 *Copyright (C) 2018 Laurent Tréguier 3 * 4 *This file is part of DLS. 5 * 6 *DLS is free software: you can redistribute it and/or modify 7 *it under the terms of the GNU General Public License as published by 8 *the Free Software Foundation, either version 3 of the License, or 9 *(at your option) any later version. 10 * 11 *DLS is distributed in the hope that it will be useful, 12 *but WITHOUT ANY WARRANTY; without even the implied warranty of 13 *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 *GNU General Public License for more details. 15 * 16 *You should have received a copy of the GNU General Public License 17 *along with DLS. If not, see <http://www.gnu.org/licenses/>. 18 * 19 */ 20 21 module dls.updater; 22 23 private immutable changelogUrl = "https://github.com/d-language-server/dls/blob/v%s/CHANGELOG.md"; 24 25 void cleanup() 26 { 27 import dls.bootstrap : dubBinDir; 28 import dls.info : currentVersion; 29 import dub.semver : compareVersions; 30 import std.file : FileException, SpanMode, dirEntries, isSymlink, remove, rmdirRecurse; 31 import std.path : baseName; 32 import std.regex : matchFirst; 33 34 foreach (string entry; dirEntries(dubBinDir, SpanMode.shallow)) 35 { 36 const match = entry.baseName.matchFirst(`dls-v([\d.]+)`); 37 38 if (match) 39 { 40 if (compareVersions(currentVersion, match[1]) > 0) 41 { 42 try 43 { 44 rmdirRecurse(entry); 45 } 46 catch (FileException e) 47 { 48 } 49 } 50 } 51 else if (isSymlink(entry)) 52 { 53 try 54 { 55 version (Windows) 56 { 57 import std.file : isDir, rmdir; 58 import std.stdio : File; 59 60 if (isDir(entry)) 61 { 62 try 63 { 64 dirEntries(entry, SpanMode.shallow); 65 } 66 catch (FileException e) 67 { 68 rmdir(entry); 69 } 70 } 71 else 72 { 73 try 74 { 75 File(entry, "rb"); 76 } 77 catch (Exception e) 78 { 79 remove(entry); 80 } 81 } 82 } 83 else version (Posix) 84 { 85 import std.file : exists, readLink; 86 87 if (!exists(readLink(entry))) 88 { 89 remove(entry); 90 } 91 } 92 else 93 { 94 static assert(false, "Platform not supported"); 95 } 96 } 97 catch (Exception e) 98 { 99 } 100 } 101 } 102 } 103 104 void update(bool autoUpdate) 105 { 106 import core.time : hours; 107 import dls.bootstrap : UpgradeFailedException, buildDls, canDownloadDls, 108 downloadDls, allReleases, linkDls; 109 import dls.info : currentVersion; 110 static import dls.protocol.jsonrpc; 111 import dls.protocol.interfaces.dls : DlsUpgradeSizeParams, TranslationParams; 112 import dls.protocol.logger : logger; 113 import dls.protocol.messages.methods : Dls; 114 import dls.protocol.messages.window : Util; 115 import dls.protocol.state : initOptions; 116 import dls.util.i18n : Tr; 117 import dub.dependency : Dependency; 118 import dub.dub : Dub, FetchOptions; 119 import dub.semver : compareVersions; 120 import std.algorithm : filter, stripLeft; 121 import std.array : array; 122 import std.concurrency : ownerTid, receiveOnly, register, send, thisTid; 123 import std.datetime : Clock, SysTime; 124 import std.format : format; 125 import std.json : JSON_TYPE; 126 import std.path : asNormalizedPath; 127 128 auto validReleases = allReleases.filter!(r => r["prerelease"].type == JSON_TYPE.FALSE 129 || initOptions.preReleaseBuilds); 130 131 if (validReleases.empty) 132 { 133 logger.warning("Unable to find any valid release"); 134 return; 135 } 136 137 immutable latestRelease = validReleases.front; 138 immutable latestVersion = latestRelease["tag_name"].str.stripLeft('v'); 139 immutable releaseTime = SysTime.fromISOExtString(latestRelease["published_at"].str); 140 141 if (latestVersion.length == 0 || compareVersions(currentVersion, 142 latestVersion) >= 0 || (Clock.currTime.toUTC() - releaseTime < 1.hours)) 143 { 144 return; 145 } 146 147 if (!autoUpdate) 148 { 149 auto id = Util.sendMessageRequest(Tr.app_upgradeDls, 150 [Tr.app_upgradeDls_upgrade], [latestVersion, currentVersion]); 151 immutable threadName = "updater"; 152 register(threadName, thisTid()); 153 send(ownerTid(), Util.ThreadMessageData(id, Tr.app_upgradeDls, threadName)); 154 155 immutable shouldUpgrade = receiveOnly!bool(); 156 157 if (!shouldUpgrade) 158 { 159 return; 160 } 161 } 162 163 dls.protocol.jsonrpc.send(Dls.UpgradeDls.didStart, 164 new TranslationParams(Tr.app_upgradeDls_upgrading)); 165 166 scope (exit) 167 { 168 dls.protocol.jsonrpc.send(Dls.UpgradeDls.didStop); 169 } 170 171 bool upgradeSuccessful; 172 173 if (canDownloadDls) 174 { 175 try 176 { 177 enum totalSizeCallback = (size_t size) { 178 dls.protocol.jsonrpc.send(Dls.UpgradeDls.didChangeTotalSize, 179 new DlsUpgradeSizeParams(Tr.app_upgradeDls_downloading, [], size)); 180 }; 181 enum chunkSizeCallback = (size_t size) { 182 dls.protocol.jsonrpc.send(Dls.UpgradeDls.didChangeCurrentSize, 183 new DlsUpgradeSizeParams(Tr.app_upgradeDls_downloading, [], size)); 184 }; 185 enum extractCallback = () { 186 dls.protocol.jsonrpc.send(Dls.UpgradeDls.didExtract, 187 new TranslationParams(Tr.app_upgradeDls_extracting)); 188 }; 189 190 downloadDls(totalSizeCallback, chunkSizeCallback, extractCallback); 191 upgradeSuccessful = true; 192 } 193 catch (Exception e) 194 { 195 logger.error("Could not download DLS: %s", e.msg); 196 } 197 } 198 199 if (!upgradeSuccessful) 200 { 201 auto dub = new Dub(); 202 FetchOptions fetchOpts; 203 fetchOpts |= FetchOptions.forceBranchUpgrade; 204 const pack = dub.fetch("dls", Dependency(">=0.0.0"), 205 dub.defaultPlacementLocation, fetchOpts); 206 207 int i; 208 immutable additionalArgs = [[], ["--force"]]; 209 210 do 211 { 212 try 213 { 214 buildDls(pack.path.toString().asNormalizedPath.array, additionalArgs[i]); 215 upgradeSuccessful = true; 216 } 217 catch (UpgradeFailedException e) 218 { 219 ++i; 220 } 221 } 222 while (i < additionalArgs.length && !upgradeSuccessful); 223 224 if (!upgradeSuccessful) 225 { 226 Util.sendMessage(Tr.app_upgradeDls_buildError); 227 return; 228 } 229 } 230 231 try 232 { 233 linkDls(); 234 auto id = Util.sendMessageRequest(Tr.app_showChangelog, 235 [Tr.app_showChangelog_show], [latestVersion]); 236 send(ownerTid(), Util.ThreadMessageData(id, Tr.app_showChangelog, 237 format!changelogUrl(latestVersion))); 238 } 239 catch (UpgradeFailedException e) 240 { 241 Util.sendMessage(Tr.app_upgradeDls_linkError); 242 } 243 }