tgstation-server 6.8.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
140 // get a lock on a system wide OD repo
141 Logger.LogTrace("Cloning OD repo...");
142
143 var progressSection1 = jobProgressReporter?.CreateSection("Updating OpenDream git repository", 0.5f);
144
145 var repo = await repositoryManager.CloneRepository(
147 null,
148 null,
149 null,
150 progressSection1,
151 true,
152 cancellationToken);
153
154 try
155 {
156 if (repo == null)
157 {
158 Logger.LogTrace("OD repo seems to already exist, attempting load and fetch...");
159 repo = await repositoryManager.LoadRepository(cancellationToken);
160
161 await repo!.FetchOrigin(
162 progressSection1,
163 null,
164 null,
165 false,
166 cancellationToken);
167 }
168
169 var progressSection2 = jobProgressReporter?.CreateSection("Checking out OpenDream version", 0.5f);
170
171 var committish = version.SourceSHA
172 ?? $"{GeneralConfiguration.OpenDreamGitTagPrefix}{version.Version!.Semver()}";
173
174 await repo.CheckoutObject(
175 committish,
176 null,
177 null,
178 true,
179 progressSection2,
180 cancellationToken);
181
182 if (!await repo.CommittishIsParent("tgs-min-compat", cancellationToken))
183 throw new JobException(ErrorCode.OpenDreamTooOld);
184
186 }
187 catch
188 {
189 repo?.Dispose();
190 throw;
191 }
192 }
193
195 public override async ValueTask Install(EngineVersion version, string installPath, bool deploymentPipelineProcesses, CancellationToken cancellationToken)
196 {
197 CheckVersionValidity(version);
198 ArgumentNullException.ThrowIfNull(installPath);
199 var sourcePath = IOManager.ConcatPath(installPath, InstallationSourceSubDirectory);
200
201 if (!await IOManager.DirectoryExists(sourcePath, cancellationToken))
202 {
203 // a zip install that didn't come from us?
204 // we want to use the bin dir, so put everything where we expect
205 Logger.LogDebug("Correcting extraction location...");
206 var dirsTask = IOManager.GetDirectories(installPath, cancellationToken);
207 var filesTask = IOManager.GetFiles(installPath, cancellationToken);
208 var dirCreateTask = IOManager.CreateDirectory(sourcePath, cancellationToken);
209
210 await Task.WhenAll(dirsTask, filesTask, dirCreateTask);
211
212 var dirsMoveTasks = dirsTask
213 .Result
214 .Select(
215 dirPath => IOManager.MoveDirectory(
216 dirPath,
218 sourcePath,
219 IOManager.GetFileName(dirPath)),
220 cancellationToken));
221 var filesMoveTask = filesTask
222 .Result
223 .Select(
224 filePath => IOManager.MoveFile(
225 filePath,
227 sourcePath,
228 IOManager.GetFileName(filePath)),
229 cancellationToken));
230
231 await Task.WhenAll(dirsMoveTasks.Concat(filesMoveTask));
232 }
233
234 var dotnetPath = await DotnetHelper.GetDotnetPath(platformIdentifier, IOManager, cancellationToken);
235 if (dotnetPath == null)
236 throw new JobException(ErrorCode.OpenDreamCantFindDotnet);
237
238 const string DeployDir = "tgs_deploy";
239 int? buildExitCode = null;
241 async shortenedPath =>
242 {
243 var shortenedDeployPath = IOManager.ConcatPath(shortenedPath, DeployDir);
244 await using var buildProcess = await ProcessExecutor.LaunchProcess(
245 dotnetPath,
246 shortenedPath,
247 $"run -c Release --project OpenDreamPackageTool -- --tgs -o {shortenedDeployPath}",
248 cancellationToken,
249 null,
250 null,
253
254 if (deploymentPipelineProcesses && SessionConfiguration.LowPriorityDeploymentProcesses)
255 buildProcess.AdjustPriority(false);
256
257 using (cancellationToken.Register(() => buildProcess.Terminate()))
258 buildExitCode = await buildProcess.Lifetime;
259
260 string? output;
262 {
263 var buildOutputTask = buildProcess.GetCombinedOutput(cancellationToken);
264 if (!buildOutputTask.IsCompleted)
265 Logger.LogTrace("OD build complete, waiting for output...");
266 output = await buildOutputTask;
267 }
268 else
269 output = "<Build output suppressed by configuration due to not being immediately available>";
270
271 Logger.LogDebug(
272 "OpenDream build exited with code {exitCode}:{newLine}{output}",
273 buildExitCode,
274 Environment.NewLine,
275 output);
276 },
277 sourcePath,
278 cancellationToken);
279
280 if (buildExitCode != 0)
281 throw new JobException("OpenDream build failed!");
282
283 var deployPath = IOManager.ConcatPath(sourcePath, DeployDir);
284 async ValueTask MoveDirs()
285 {
286 var dirs = await IOManager.GetDirectories(deployPath, cancellationToken);
287 await Task.WhenAll(
288 dirs.Select(
290 dir,
292 installPath,
294 cancellationToken)));
295 }
296
297 async ValueTask MoveFiles()
298 {
299 var files = await IOManager.GetFiles(deployPath, cancellationToken);
300 await Task.WhenAll(
301 files.Select(
302 file => IOManager.MoveFile(
303 file,
305 installPath,
306 IOManager.GetFileName(file)),
307 cancellationToken)));
308 }
309
310 var dirsMoveTask = MoveDirs();
311 var outputFilesMoveTask = MoveFiles();
312 await ValueTaskExtensions.WhenAll(dirsMoveTask, outputFilesMoveTask);
313 await IOManager.DeleteDirectory(sourcePath, cancellationToken);
314 }
315
317 public override ValueTask UpgradeInstallation(EngineVersion version, string path, CancellationToken cancellationToken)
318 {
319 CheckVersionValidity(version);
320 ArgumentNullException.ThrowIfNull(path);
321 return ValueTask.CompletedTask;
322 }
323
325 public override ValueTask TrustDmbPath(EngineVersion engineVersion, string fullDmbPath, CancellationToken cancellationToken)
326 {
327 ArgumentNullException.ThrowIfNull(engineVersion);
328 ArgumentNullException.ThrowIfNull(fullDmbPath);
329
330 Logger.LogTrace("TrustDmbPath is a no-op: {path}", fullDmbPath);
331 return ValueTask.CompletedTask;
332 }
333
341 protected virtual ValueTask HandleExtremelyLongPathOperation(
342 Func<string, ValueTask> shortenedPathOperation,
343 string originalPath,
344 CancellationToken cancellationToken)
345 => shortenedPathOperation(originalPath); // based god linux has no such weakness
346
353 protected void GetExecutablePaths(string installationPath, out string serverExePath, out string compilerExePath)
354 {
355 var exeExtension = platformIdentifier.IsWindows
356 ? ".exe"
357 : String.Empty;
358
359 serverExePath = IOManager.ConcatPath(
360 installationPath,
361 BinDir,
362 ServerDir,
363 $"Robust.Server{exeExtension}");
364
365 compilerExePath = IOManager.ConcatPath(
366 installationPath,
367 BinDir,
369 $"DMCompiler{exeExtension}");
370 }
371 }
372}
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 async ValueTask< IEngineInstallationData > DownloadVersion(EngineVersion version, JobProgressReporter? jobProgressReporter, CancellationToken cancellationToken)
Download a given engine version . A ValueTask<TResult> resulting in the IEngineInstallationData for t...
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 run...
const string ServerDir
The OD server directory name.
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 operatio...
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.
Implementation of IEngineInstallationData using a IRepository.
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.
Definition: JobException.cs:11
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.
Definition: DotnetHelper.cs:14
static async ValueTask< string?> GetDotnetPath(IPlatformIdentifier platformIdentifier, IIOManager ioManager, CancellationToken cancellationToken)
Locate a dotnet executable to use.
Definition: DotnetHelper.cs:22
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.
Factory for creating and loading IRepositorys.
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