tgstation-server 6.12.3
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 async ValueTask<IEngineInstallation> CreateInstallation(EngineVersion version, string path, Task installationTask, CancellationToken cancellationToken)
122 {
123 CheckVersionValidity(version);
124 GetExecutablePaths(path, out var serverExePath, out var compilerExePath);
125
126 var dotnetPath = (await DotnetHelper.GetDotnetPath(platformIdentifier, IOManager, cancellationToken))
127 ?? throw new JobException("Failed to find dotnet path!");
128 return new OpenDreamInstallation(
129 new ResolvingIOManager(IOManager, path),
132 dotnetPath,
133 serverExePath,
134 compilerExePath,
135 installationTask,
136 version);
137 }
138
140 public override async ValueTask<IEngineInstallationData> DownloadVersion(EngineVersion version, JobProgressReporter jobProgressReporter, CancellationToken cancellationToken)
141 {
142 CheckVersionValidity(version);
143 ArgumentNullException.ThrowIfNull(jobProgressReporter);
144
145 // get a lock on a system wide OD repo
146 Logger.LogTrace("Cloning OD repo...");
147
148 var progressSection1 = jobProgressReporter.CreateSection("Updating OpenDream git repository", 0.5f);
149 IRepository? repo;
150 try
151 {
154 null,
155 null,
156 null,
157 progressSection1,
158 true,
159 cancellationToken);
160 }
161 catch
162 {
163 progressSection1.Dispose();
164 throw;
165 }
166
167 try
168 {
169 if (repo == null)
170 {
171 Logger.LogTrace("OD repo seems to already exist, attempting load and fetch...");
172 repo = await repositoryManager.LoadRepository(cancellationToken);
173 if (repo == null)
174 throw new JobException("Can't load OpenDream repository! Please delete cache from disk!");
175
176 await repo!.FetchOrigin(
177 progressSection1,
178 null,
179 null,
180 false,
181 cancellationToken);
182 }
183
184 progressSection1.Dispose();
185 progressSection1 = null;
186
187 using (var progressSection2 = jobProgressReporter.CreateSection("Checking out OpenDream version", 0.5f))
188 {
189 var committish = version.SourceSHA
190 ?? $"{GeneralConfiguration.OpenDreamGitTagPrefix}{version.Version!.Semver()}";
191
192 await repo.CheckoutObject(
193 committish,
194 null,
195 null,
196 true,
197 false,
198 progressSection2,
199 cancellationToken);
200 }
201
202 if (!await repo.CommittishIsParent("tgs-min-compat", cancellationToken))
203 throw new JobException(ErrorCode.OpenDreamTooOld);
204
206 }
207 catch
208 {
209 repo?.Dispose();
210 throw;
211 }
212 finally
213 {
214 progressSection1?.Dispose();
215 }
216 }
217
219 public override async ValueTask Install(EngineVersion version, string installPath, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
220 {
221 CheckVersionValidity(version);
222 ArgumentNullException.ThrowIfNull(installPath);
223 var sourcePath = IOManager.ConcatPath(installPath, InstallationSourceSubDirectory);
224
225 if (!await IOManager.DirectoryExists(sourcePath, cancellationToken))
226 {
227 // a zip install that didn't come from us?
228 // we want to use the bin dir, so put everything where we expect
229 Logger.LogDebug("Correcting extraction location...");
230 var dirsTask = IOManager.GetDirectories(installPath, cancellationToken);
231 var filesTask = IOManager.GetFiles(installPath, cancellationToken);
232 var dirCreateTask = IOManager.CreateDirectory(sourcePath, cancellationToken);
233
234 await Task.WhenAll(dirsTask, filesTask, dirCreateTask);
235
236 var dirsMoveTasks = dirsTask
237 .Result
238 .Select(
239 dirPath => IOManager.MoveDirectory(
240 dirPath,
242 sourcePath,
243 IOManager.GetFileName(dirPath)),
244 cancellationToken));
245 var filesMoveTask = filesTask
246 .Result
247 .Select(
248 filePath => IOManager.MoveFile(
249 filePath,
251 sourcePath,
252 IOManager.GetFileName(filePath)),
253 cancellationToken));
254
255 await Task.WhenAll(dirsMoveTasks.Concat(filesMoveTask));
256 }
257
258 var dotnetPath = (await DotnetHelper.GetDotnetPath(platformIdentifier, IOManager, cancellationToken))
259 ?? throw new JobException(ErrorCode.OpenDreamCantFindDotnet);
260 const string DeployDir = "tgs_deploy";
261 int? buildExitCode = null;
263 async shortenedPath =>
264 {
265 var shortenedDeployPath = IOManager.ConcatPath(shortenedPath, DeployDir);
266 await using var buildProcess = await ProcessExecutor.LaunchProcess(
267 dotnetPath,
268 shortenedPath,
269 $"run -c Release --project OpenDreamPackageTool -- --tgs -o {shortenedDeployPath}",
270 cancellationToken,
271 null,
272 null,
275
276 if (deploymentPipelineProcesses && SessionConfiguration.LowPriorityDeploymentProcesses)
277 buildProcess.AdjustPriority(false);
278
279 using (cancellationToken.Register(() => buildProcess.Terminate()))
280 buildExitCode = await buildProcess.Lifetime;
281
282 string? output;
284 {
285 var buildOutputTask = buildProcess.GetCombinedOutput(cancellationToken);
286 if (!buildOutputTask.IsCompleted)
287 Logger.LogTrace("OD build complete, waiting for output...");
288 output = await buildOutputTask;
289 }
290 else
291 output = "<Build output suppressed by configuration due to not being immediately available>";
292
293 Logger.LogDebug(
294 "OpenDream build exited with code {exitCode}:{newLine}{output}",
295 buildExitCode,
296 Environment.NewLine,
297 output);
298 },
299 sourcePath,
300 cancellationToken);
301
302 if (buildExitCode != 0)
303 throw new JobException("OpenDream build failed!");
304
305 var deployPath = IOManager.ConcatPath(sourcePath, DeployDir);
306 async ValueTask MoveDirs()
307 {
308 var dirs = await IOManager.GetDirectories(deployPath, cancellationToken);
309 await Task.WhenAll(
310 dirs.Select(
312 dir,
314 installPath,
316 cancellationToken)));
317 }
318
319 async ValueTask MoveFiles()
320 {
321 var files = await IOManager.GetFiles(deployPath, cancellationToken);
322 await Task.WhenAll(
323 files.Select(
324 file => IOManager.MoveFile(
325 file,
327 installPath,
328 IOManager.GetFileName(file)),
329 cancellationToken)));
330 }
331
332 var dirsMoveTask = MoveDirs();
333 var outputFilesMoveTask = MoveFiles();
334 await ValueTaskExtensions.WhenAll(dirsMoveTask, outputFilesMoveTask);
335 await IOManager.DeleteDirectory(sourcePath, cancellationToken);
336 }
337
339 public override ValueTask UpgradeInstallation(EngineVersion version, string path, CancellationToken cancellationToken)
340 {
341 CheckVersionValidity(version);
342 ArgumentNullException.ThrowIfNull(path);
343 return ValueTask.CompletedTask;
344 }
345
347 public override ValueTask TrustDmbPath(EngineVersion engineVersion, string fullDmbPath, CancellationToken cancellationToken)
348 {
349 ArgumentNullException.ThrowIfNull(engineVersion);
350 ArgumentNullException.ThrowIfNull(fullDmbPath);
351
352 Logger.LogTrace("TrustDmbPath is a no-op: {path}", fullDmbPath);
353 return ValueTask.CompletedTask;
354 }
355
363 protected virtual ValueTask HandleExtremelyLongPathOperation(
364 Func<string, ValueTask> shortenedPathOperation,
365 string originalPath,
366 CancellationToken cancellationToken)
367 => shortenedPathOperation(originalPath); // based god linux has no such weakness
368
375 protected void GetExecutablePaths(string installationPath, out string serverExePath, out string compilerExePath)
376 {
377 const string DllExtension = ".dll";
378
379 serverExePath = IOManager.ConcatPath(
380 installationPath,
381 BinDir,
382 ServerDir,
383 $"Robust.Server{DllExtension}");
384
385 compilerExePath = IOManager.ConcatPath(
386 installationPath,
387 BinDir,
389 $"DMCompiler{DllExtension}");
390 }
391 }
392}
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 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.
override async ValueTask< IEngineInstallation > CreateInstallation(EngineVersion version, string path, Task installationTask, CancellationToken cancellationToken)
Creates an IEngineInstallation for a given version .A ValueTask<TResult> resulting in the IEngineInst...
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