tgstation-server 6.12.0
The /tg/station 13 server suite
Loading...
Searching...
No Matches
OpenDreamInstaller.cs
Go to the documentation of this file.
1using System;
2using System.Linq;
3using System.Threading;
4using System.Threading.Tasks;
5
6using Microsoft.Extensions.Logging;
7using Microsoft.Extensions.Options;
8
18
20{
25 {
29 const string BinDir = "bin";
30
34 const string ServerDir = "server";
35
39 const string InstallationCompilerDirectory = "compiler";
40
44 const string InstallationSourceSubDirectory = "TgsSourceSubdir";
45
47 protected override EngineType TargetEngineType => EngineType.OpenDream;
48
53
58
63
68
73
78
83
97 IIOManager ioManager,
98 ILogger<OpenDreamInstaller> logger,
100 IProcessExecutor processExecutor,
104 IOptions<GeneralConfiguration> generalConfigurationOptions,
105 IOptions<SessionConfiguration> sessionConfigurationOptions)
106 : base(ioManager, logger)
107 {
108 this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
109 ProcessExecutor = processExecutor ?? throw new ArgumentNullException(nameof(processExecutor));
110 this.repositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager));
111 this.asyncDelayer = asyncDelayer ?? throw new ArgumentNullException(nameof(asyncDelayer));
112 this.httpClientFactory = httpClientFactory ?? throw new ArgumentNullException(nameof(httpClientFactory));
113 GeneralConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
114 SessionConfiguration = sessionConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(sessionConfigurationOptions));
115 }
116
118 public override Task CleanCache(CancellationToken cancellationToken) => Task.CompletedTask;
119
121 public override IEngineInstallation CreateInstallation(EngineVersion version, string path, Task installationTask)
122 {
123 CheckVersionValidity(version);
124 GetExecutablePaths(path, out var serverExePath, out var compilerExePath);
125 return new OpenDreamInstallation(
126 new ResolvingIOManager(IOManager, path),
129 serverExePath,
130 compilerExePath,
131 installationTask,
132 version);
133 }
134
136 public override async ValueTask<IEngineInstallationData> DownloadVersion(EngineVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken)
137 {
138 CheckVersionValidity(version);
139 ArgumentNullException.ThrowIfNull(jobProgressReporter);
140
141 // get a lock on a system wide OD repo
142 Logger.LogTrace("Cloning OD repo...");
143
144 var progressSection1 = jobProgressReporter.CreateSection("Updating OpenDream git repository", 0.5f);
145 IRepository? repo;
146 try
147 {
150 null,
151 null,
152 null,
153 progressSection1,
154 true,
155 cancellationToken);
156 }
157 catch
158 {
159 progressSection1.Dispose();
160 throw;
161 }
162
163 try
164 {
165 if (repo == null)
166 {
167 Logger.LogTrace("OD repo seems to already exist, attempting load and fetch...");
168 repo = await repositoryManager.LoadRepository(cancellationToken);
169 if (repo == null)
170 throw new JobException("Can't load OpenDream repository! Please delete cache from disk!");
171
172 await repo!.FetchOrigin(
173 progressSection1,
174 null,
175 null,
176 false,
177 cancellationToken);
178 }
179
180 progressSection1.Dispose();
181 progressSection1 = null;
182
183 using (var progressSection2 = jobProgressReporter.CreateSection("Checking out OpenDream version", 0.5f))
184 {
185 var committish = version.SourceSHA
186 ?? $"{GeneralConfiguration.OpenDreamGitTagPrefix}{version.Version!.Semver()}";
187
188 await repo.CheckoutObject(
189 committish,
190 null,
191 null,
192 true,
193 false,
194 progressSection2,
195 cancellationToken);
196 }
197
198 if (!await repo.CommittishIsParent("tgs-min-compat", cancellationToken))
199 throw new JobException(ErrorCode.OpenDreamTooOld);
200
202 }
203 catch
204 {
205 repo?.Dispose();
206 throw;
207 }
208 finally
209 {
210 progressSection1?.Dispose();
211 }
212 }
213
215 public override async ValueTask Install(EngineVersion version, string installPath, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
216 {
217 CheckVersionValidity(version);
218 ArgumentNullException.ThrowIfNull(installPath);
219 var sourcePath = IOManager.ConcatPath(installPath, InstallationSourceSubDirectory);
220
221 if (!await IOManager.DirectoryExists(sourcePath, cancellationToken))
222 {
223 // a zip install that didn't come from us?
224 // we want to use the bin dir, so put everything where we expect
225 Logger.LogDebug("Correcting extraction location...");
226 var dirsTask = IOManager.GetDirectories(installPath, cancellationToken);
227 var filesTask = IOManager.GetFiles(installPath, cancellationToken);
228 var dirCreateTask = IOManager.CreateDirectory(sourcePath, cancellationToken);
229
230 await Task.WhenAll(dirsTask, filesTask, dirCreateTask);
231
232 var dirsMoveTasks = dirsTask
233 .Result
234 .Select(
235 dirPath => IOManager.MoveDirectory(
236 dirPath,
238 sourcePath,
239 IOManager.GetFileName(dirPath)),
240 cancellationToken));
241 var filesMoveTask = filesTask
242 .Result
243 .Select(
244 filePath => IOManager.MoveFile(
245 filePath,
247 sourcePath,
248 IOManager.GetFileName(filePath)),
249 cancellationToken));
250
251 await Task.WhenAll(dirsMoveTasks.Concat(filesMoveTask));
252 }
253
254 var dotnetPath = (await DotnetHelper.GetDotnetPath(platformIdentifier, IOManager, cancellationToken))
255 ?? throw new JobException(ErrorCode.OpenDreamCantFindDotnet);
256 const string DeployDir = "tgs_deploy";
257 int? buildExitCode = null;
259 async shortenedPath =>
260 {
261 var shortenedDeployPath = IOManager.ConcatPath(shortenedPath, DeployDir);
262 await using var buildProcess = await ProcessExecutor.LaunchProcess(
263 dotnetPath,
264 shortenedPath,
265 $"run -c Release --project OpenDreamPackageTool -- --tgs -o {shortenedDeployPath}",
266 cancellationToken,
267 null,
268 null,
271
272 if (deploymentPipelineProcesses && SessionConfiguration.LowPriorityDeploymentProcesses)
273 buildProcess.AdjustPriority(false);
274
275 using (cancellationToken.Register(() => buildProcess.Terminate()))
276 buildExitCode = await buildProcess.Lifetime;
277
278 string? output;
280 {
281 var buildOutputTask = buildProcess.GetCombinedOutput(cancellationToken);
282 if (!buildOutputTask.IsCompleted)
283 Logger.LogTrace("OD build complete, waiting for output...");
284 output = await buildOutputTask;
285 }
286 else
287 output = "<Build output suppressed by configuration due to not being immediately available>";
288
289 Logger.LogDebug(
290 "OpenDream build exited with code {exitCode}:{newLine}{output}",
291 buildExitCode,
292 Environment.NewLine,
293 output);
294 },
295 sourcePath,
296 cancellationToken);
297
298 if (buildExitCode != 0)
299 throw new JobException("OpenDream build failed!");
300
301 var deployPath = IOManager.ConcatPath(sourcePath, DeployDir);
302 async ValueTask MoveDirs()
303 {
304 var dirs = await IOManager.GetDirectories(deployPath, cancellationToken);
305 await Task.WhenAll(
306 dirs.Select(
308 dir,
310 installPath,
312 cancellationToken)));
313 }
314
315 async ValueTask MoveFiles()
316 {
317 var files = await IOManager.GetFiles(deployPath, cancellationToken);
318 await Task.WhenAll(
319 files.Select(
320 file => IOManager.MoveFile(
321 file,
323 installPath,
324 IOManager.GetFileName(file)),
325 cancellationToken)));
326 }
327
328 var dirsMoveTask = MoveDirs();
329 var outputFilesMoveTask = MoveFiles();
330 await ValueTaskExtensions.WhenAll(dirsMoveTask, outputFilesMoveTask);
331 await IOManager.DeleteDirectory(sourcePath, cancellationToken);
332 }
333
335 public override ValueTask UpgradeInstallation(EngineVersion version, string path, CancellationToken cancellationToken)
336 {
337 CheckVersionValidity(version);
338 ArgumentNullException.ThrowIfNull(path);
339 return ValueTask.CompletedTask;
340 }
341
343 public override ValueTask TrustDmbPath(EngineVersion engineVersion, string fullDmbPath, CancellationToken cancellationToken)
344 {
345 ArgumentNullException.ThrowIfNull(engineVersion);
346 ArgumentNullException.ThrowIfNull(fullDmbPath);
347
348 Logger.LogTrace("TrustDmbPath is a no-op: {path}", fullDmbPath);
349 return ValueTask.CompletedTask;
350 }
351
359 protected virtual ValueTask HandleExtremelyLongPathOperation(
360 Func<string, ValueTask> shortenedPathOperation,
361 string originalPath,
362 CancellationToken cancellationToken)
363 => shortenedPathOperation(originalPath); // based god linux has no such weakness
364
371 protected void GetExecutablePaths(string installationPath, out string serverExePath, out string compilerExePath)
372 {
373 var exeExtension = platformIdentifier.IsWindows
374 ? ".exe"
375 : String.Empty;
376
377 serverExePath = IOManager.ConcatPath(
378 installationPath,
379 BinDir,
380 ServerDir,
381 $"Robust.Server{exeExtension}");
382
383 compilerExePath = IOManager.ConcatPath(
384 installationPath,
385 BinDir,
387 $"DMCompiler{exeExtension}");
388 }
389 }
390}
Information about an engine installation.
Extension methods for the ValueTask and ValueTask<TResult> classes.
static async ValueTask WhenAll(IEnumerable< ValueTask > tasks)
Fully await a given list of tasks .
void CheckVersionValidity(EngineVersion version)
Check that a given version is of type EngineType.Byond.
IIOManager IOManager
Gets the IIOManager for the EngineInstallerBase.
ILogger< EngineInstallerBase > Logger
Gets the ILogger for the EngineInstallerBase.
Implementation of IEngineInstallation for EngineType.OpenDream.
Implementation of IEngineInstaller for EngineType.OpenDream.
void GetExecutablePaths(string installationPath, out string serverExePath, out string compilerExePath)
Gets the paths to the server and client executables.
readonly IAbstractHttpClientFactory httpClientFactory
The IAbstractHttpClientFactory for the OpenDreamInstaller.
readonly IPlatformIdentifier platformIdentifier
The IPlatformIdentifier for the OpenDreamInstaller.
readonly IRepositoryManager repositoryManager
The IRepositoryManager for the OpenDream repository.
override async ValueTask Install(EngineVersion version, string installPath, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
Does actions necessary to get an extracted installation working.A ValueTask representing the running ...
override ValueTask TrustDmbPath(EngineVersion engineVersion, string fullDmbPath, CancellationToken cancellationToken)
Add a given fullDmbPath to the trusted DMBs list in BYOND's config.A ValueTask representing the runn...
const string ServerDir
The OD server directory name.
override async ValueTask< IEngineInstallationData > DownloadVersion(EngineVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken)
Download a given engine version .A ValueTask<TResult> resulting in the IEngineInstallationData for th...
override IEngineInstallation CreateInstallation(EngineVersion version, string path, Task installationTask)
Creates an IEngineInstallation for a given version .The IEngineInstallation.
override Task CleanCache(CancellationToken cancellationToken)
Attempts to cleans the engine's cache folder for the system.A Task representing the running operation...
OpenDreamInstaller(IIOManager ioManager, ILogger< OpenDreamInstaller > logger, IPlatformIdentifier platformIdentifier, IProcessExecutor processExecutor, IRepositoryManager repositoryManager, IAsyncDelayer asyncDelayer, IAbstractHttpClientFactory httpClientFactory, IOptions< GeneralConfiguration > generalConfigurationOptions, IOptions< SessionConfiguration > sessionConfigurationOptions)
Initializes a new instance of the OpenDreamInstaller class.
const string InstallationCompilerDirectory
The name of the subdirectory in an installation's BinDir used to store the compiler binaries.
const string InstallationSourceSubDirectory
The name of the subdirectory used for the RepositoryEngineInstallationData's copy.
override ValueTask UpgradeInstallation(EngineVersion version, string path, CancellationToken cancellationToken)
Does actions necessary to get upgrade a version installed by a previous version of TGS....
readonly IAsyncDelayer asyncDelayer
The IAsyncDelayer for the OpenDreamInstaller.
virtual ValueTask HandleExtremelyLongPathOperation(Func< string, ValueTask > shortenedPathOperation, string originalPath, CancellationToken cancellationToken)
Perform an operation on a very long path.
bool OpenDreamSuppressInstallOutput
If the dotnet output of creating an OpenDream installation should be suppressed. Known to cause issue...
Uri OpenDreamGitUrl
Location of a publically accessible OpenDream repository.
Configuration options for the game sessions.
bool LowPriorityDeploymentProcesses
If the deployment DreamMaker and DreamDaemon instances are set to be below normal priority processes.
An IIOManager that resolve relative paths from another IIOManager to a subdirectory of that.
Operation exceptions thrown from the context of a Models.Job.
JobProgressReporter CreateSection(string? newStageName, double percentage)
Create a subsection of the JobProgressReporter with its optional own stage name.
Helper methods for working with the dotnet executable.
static async ValueTask< string?> GetDotnetPath(IPlatformIdentifier platformIdentifier, IIOManager ioManager, CancellationToken cancellationToken)
Locate a dotnet executable to use.
async ValueTask< IProcess > LaunchProcess(string fileName, string workingDirectory, string arguments, CancellationToken cancellationToken, IReadOnlyDictionary< string, string >? environment, string? fileRedirect, bool readStandardHandles, bool noShellExecute)
Launch a IProcess.A ValueTask<TResult> resulting in the new IProcess.
Represents an on-disk git repository.
Task< bool > CommittishIsParent(string committish, CancellationToken cancellationToken)
Check if a given committish is a parent of the current Head.
ValueTask CheckoutObject(string committish, string? username, string? password, bool updateSubmodules, bool moveCurrentReference, JobProgressReporter progressReporter, CancellationToken cancellationToken)
Checks out a given committish .
ValueTask FetchOrigin(JobProgressReporter progressReporter, string? username, string? password, bool deploymentPipeline, CancellationToken cancellationToken)
Fetch commits from the origin repository.
ValueTask< IRepository?> CloneRepository(Uri url, string? initialBranch, string? username, string? password, JobProgressReporter progressReporter, bool recurseSubmodules, CancellationToken cancellationToken)
Clone the repository at url .
ValueTask< IRepository?> LoadRepository(CancellationToken cancellationToken)
Attempt to load the IRepository from the default location.
Interface for using filesystems.
Definition IIOManager.cs:13
Task< IReadOnlyList< string > > GetFiles(string path, CancellationToken cancellationToken)
Returns full file names in a given path .
string GetFileName(string path)
Gets the file name portion of a path .
string ConcatPath(params string[] paths)
Combines an array of strings into a path.
Task MoveFile(string source, string destination, CancellationToken cancellationToken)
Moves a file at source to destination .
Task< IReadOnlyList< string > > GetDirectories(string path, CancellationToken cancellationToken)
Returns full directory names in a given path .
Task CreateDirectory(string path, CancellationToken cancellationToken)
Create a directory at path .
Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory, removes and does not enter any symlinks encounterd.
Task MoveDirectory(string source, string destination, CancellationToken cancellationToken)
Moves a directory at source to destination .
Task< bool > DirectoryExists(string path, CancellationToken cancellationToken)
Check that the directory at path exists.
For identifying the current platform.
ErrorCode
Types of Response.ErrorMessageResponses that the API may return.
Definition ErrorCode.cs:12
EngineType
The type of engine the codebase is using.
Definition EngineType.cs:7