From 14308a40f8a596ff658f50d8172c4b23f619a132 Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 17:46:52 -0400
Subject: [PATCH 01/24] Create dependabot.yml for automatic Nuget update PRs
---
.github/dependabot.yml | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 .github/dependabot.yml
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000000..41bca98247
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,10 @@
+# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
+version: 2
+updates:
+ - package-ecosystem: "NuGet"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ labels:
+ - "Dependencies"
+ open-pull-requests-limit: 100
From 25776270c350f17914b89507c4c43a74eda4ab9b Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 19:22:50 -0400
Subject: [PATCH 02/24] Add `IIOManager.PathIsChildOf`
---
.../IO/DefaultIOManager.cs | 27 +++++++++++++++++++
src/Tgstation.Server.Host/IO/IIOManager.cs | 11 +++++++-
2 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
index 0ce1d50f45..b392bb7654 100644
--- a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs
@@ -358,6 +358,33 @@ namespace Tgstation.Server.Host.IO
DefaultBufferSize,
true);
+ ///
+ public Task PathIsChildOf(string parentPath, string childPath, CancellationToken cancellationToken) => Task.Factory.StartNew(
+ () =>
+ {
+ parentPath = ResolvePath(parentPath);
+ childPath = ResolvePath(childPath);
+
+ if (parentPath == childPath)
+ return true;
+
+ // https://stackoverflow.com/questions/5617320/given-full-path-check-if-path-is-subdirectory-of-some-other-path-or-otherwise?lq=1
+ var di1 = new DirectoryInfo(parentPath);
+ var di2 = new DirectoryInfo(childPath);
+ while (di2.Parent != null)
+ {
+ if (di2.Parent.FullName == di1.FullName)
+ return true;
+
+ di2 = di2.Parent;
+ }
+
+ return false;
+ },
+ cancellationToken,
+ BlockingTaskCreationOptions,
+ TaskScheduler.Current);
+
///
/// Copies a directory from to .
///
diff --git a/src/Tgstation.Server.Host/IO/IIOManager.cs b/src/Tgstation.Server.Host/IO/IIOManager.cs
index e14f15cf29..13a29310e1 100644
--- a/src/Tgstation.Server.Host/IO/IIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/IIOManager.cs
@@ -45,6 +45,15 @@ namespace Tgstation.Server.Host.IO
/// if contains a '..' accessor, otherwise.
bool PathContainsParentAccess(string path);
+ ///
+ /// Check if a given is a parent of a given .
+ ///
+ /// The parent path.
+ /// The child path.
+ /// The for the operation.
+ /// A resulting in if is a child of or they are equivalent.
+ Task PathIsChildOf(string parentPath, string childPath, CancellationToken cancellationToken);
+
///
/// Copies a directory from to .
///
@@ -68,7 +77,7 @@ namespace Tgstation.Server.Host.IO
///
/// The file to check for existence.
/// The for the operation.
- /// A resulting in if the file at exists, otherwise.
+ /// A resulting in if the file at exists, otherwise.
Task FileExists(string path, CancellationToken cancellationToken);
///
From 39532b182872681f3c8da33bd9d4e8208732ea5d Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 21:28:45 -0400
Subject: [PATCH 03/24] Fix ecosystem specifier
---
.github/dependabot.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 41bca98247..5c3351535e 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,7 +1,7 @@
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- - package-ecosystem: "NuGet"
+ - package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "daily"
From 7186eabb0f5918451229d0d226b3af90732bde33 Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 22:28:14 -0400
Subject: [PATCH 04/24] Make dependabot use the secure CI Pipeline
---
.github/workflows/ci-pipeline.yml | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml
index 9a7b747137..c5f3b2b007 100644
--- a/.github/workflows/ci-pipeline.yml
+++ b/.github/workflows/ci-pipeline.yml
@@ -55,14 +55,20 @@ jobs:
runs-on: ubuntu-latest
permissions:
pull-requests: write
- if: github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id && github.event.pull_request.state == 'open'
+ if: github.event_name == 'pull_request_target' && (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id || github.event.pull_request.user.id == 49699333) && github.event.pull_request.state == 'open'
steps:
- name: Comment on new Fork PR
- if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared')
+ if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') && github.event.pull_request.user.id != 49699333
uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308
with:
message: Thank you for contributing to ${{ github.event.pull_request.base.repo.name }}! The workflow '${{ github.workflow }}' requires repository secrets and will not run without approval. Maintainers can add the `CI Cleared` label to allow it to run. Please note that any changes to the workflow file will not be reflected in the run.
+ - name: Comment on dependabot PR
+ if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') && github.event.pull_request.user.id == 49699333
+ uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308
+ with:
+ message: Check for supply chain attacks then add the `CI Cleared` label to allow CI to run.
+
- name: "Remove Stale 'CI Cleared' Label"
if: github.event.action == 'synchronize' || github.event.action == 'reopened'
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0
@@ -82,7 +88,7 @@ jobs:
with:
labels: CI Approval Required
- - name: Fail Clearance Check if PR has Unlabeled new Commits from Fork
+ - name: Fail Clearance Check if PR has Unlabeled new Commits from User
if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && !contains(github.event.pull_request.labels.*.name, 'CI Cleared'))
run: exit 1
@@ -90,7 +96,7 @@ jobs:
name: CI Start Gate
needs: security-checkpoint
runs-on: ubuntu-latest
- if: (!(cancelled() || failure()) && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.event_name == 'schedule' || (github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id && github.event_name != 'pull_request_target')))))
+ if: (!(cancelled() || failure()) && (needs.security-checkpoint.result == 'success' || (needs.security-checkpoint.result == 'skipped' && (github.event_name == 'push' || github.event_name == 'schedule' || ((github.event.pull_request.head.repo.id == github.event.pull_request.base.repo.id && github.event.pull_request.user.id != 49699333) && github.event_name != 'pull_request_target')))))
steps:
- name: GitHub Requires at Least One Step for a Job
run: exit 0
From 740173b995a68db197a5c4b92331e57fec83bca0 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 25 Jul 2024 02:31:25 +0000
Subject: [PATCH 05/24] Bump Microsoft.IdentityModel.JsonWebTokens from 8.0.0
to 8.0.1
Bumps [Microsoft.IdentityModel.JsonWebTokens](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 8.0.0 to 8.0.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/8.0.0...8.0.1)
---
updated-dependencies:
- dependency-name: Microsoft.IdentityModel.JsonWebTokens
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
src/Tgstation.Server.Api/Tgstation.Server.Api.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj
index b55dff5e5f..0a2e4679bd 100644
--- a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj
+++ b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj
@@ -28,7 +28,7 @@
-
+
From 1759cf1141f2d3f1e577c187c873576e3b83f783 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 25 Jul 2024 02:34:18 +0000
Subject: [PATCH 06/24] Bump Z.EntityFramework.Plus.EFCore from 8.103.0 to
8.103.1
Bumps [Z.EntityFramework.Plus.EFCore](https://github.com/zzzprojects/EntityFramework-Plus) from 8.103.0 to 8.103.1.
- [Release notes](https://github.com/zzzprojects/EntityFramework-Plus/releases)
- [Commits](https://github.com/zzzprojects/EntityFramework-Plus/compare/8.103.0.0...8.103.1.0)
---
updated-dependencies:
- dependency-name: Z.EntityFramework.Plus.EFCore
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
src/Tgstation.Server.Host/Tgstation.Server.Host.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
index e29c37faa7..197583214f 100644
--- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
+++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj
@@ -125,7 +125,7 @@
-
+
From b1cf675992cdd4c7654bc7305968cc8680c3b2ac Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 22:38:41 -0400
Subject: [PATCH 07/24] Improve maintainer message
---
.github/workflows/ci-pipeline.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml
index c5f3b2b007..4c355373f3 100644
--- a/.github/workflows/ci-pipeline.yml
+++ b/.github/workflows/ci-pipeline.yml
@@ -67,7 +67,7 @@ jobs:
if: github.event.action == 'opened' && !contains(github.event.pull_request.labels.*.name, 'CI Cleared') && github.event.pull_request.user.id == 49699333
uses: thollander/actions-comment-pull-request@1d3973dc4b8e1399c0620d3f2b1aa5e795465308
with:
- message: Check for supply chain attacks then add the `CI Cleared` label to allow CI to run.
+ message: Set the milestone to the next minor version, check for supply chain attacks, and then add the `CI Cleared` label to allow CI to run.
- name: "Remove Stale 'CI Cleared' Label"
if: github.event.action == 'synchronize' || github.event.action == 'reopened'
From 3fc098ce5aba9f28d98865824f0b3d0075ba8c57 Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 22:45:24 -0400
Subject: [PATCH 08/24] Fix CI Approval Required label status
---
.github/workflows/ci-pipeline.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml
index 4c355373f3..b6a4d40c47 100644
--- a/.github/workflows/ci-pipeline.yml
+++ b/.github/workflows/ci-pipeline.yml
@@ -83,7 +83,7 @@ jobs:
github_token: ${{ github.token }}
- name: "Remove 'CI Approval Required' Label"
- if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && !contains(github.event.pull_request.labels.*.name, 'CI Cleared'))
+ if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && contains(github.event.pull_request.labels.*.name, 'CI Cleared'))
uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0
with:
labels: CI Approval Required
From 38a356902937618dbd2905bd818f1604ae0093c1 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 25 Jul 2024 11:09:19 +0000
Subject: [PATCH 09/24] Bump WixToolset.Dtf.CustomAction from 4.0.4 to 5.0.1
Bumps [WixToolset.Dtf.CustomAction](https://github.com/wixtoolset/wix) from 4.0.4 to 5.0.1.
- [Release notes](https://github.com/wixtoolset/wix/releases)
- [Commits](https://github.com/wixtoolset/wix/compare/v4.0.4...v5.0.1)
---
updated-dependencies:
- dependency-name: WixToolset.Dtf.CustomAction
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.../Tgstation.Server.Host.Service.Wix.Extensions.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/Tgstation.Server.Host.Service.Wix.Extensions.csproj b/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/Tgstation.Server.Host.Service.Wix.Extensions.csproj
index 85cbe7e02f..bf40086eea 100644
--- a/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/Tgstation.Server.Host.Service.Wix.Extensions.csproj
+++ b/build/package/winget/Tgstation.Server.Host.Service.Wix.Extensions/Tgstation.Server.Host.Service.Wix.Extensions.csproj
@@ -7,7 +7,7 @@
-
+
From 6ef6faa4c4e404c54364535ca73dfadac3bfe2e9 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 26 Jul 2024 05:09:50 +0000
Subject: [PATCH 10/24] Bump Microsoft.EntityFrameworkCore.InMemory from 8.0.6
to 8.0.7
Bumps [Microsoft.EntityFrameworkCore.InMemory](https://github.com/dotnet/efcore) from 8.0.6 to 8.0.7.
- [Release notes](https://github.com/dotnet/efcore/releases)
- [Commits](https://github.com/dotnet/efcore/compare/v8.0.6...v8.0.7)
---
updated-dependencies:
- dependency-name: Microsoft.EntityFrameworkCore.InMemory
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
.../Tgstation.Server.Host.Tests.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj b/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj
index 326b6f38b2..5e2a7f71e9 100644
--- a/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj
+++ b/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj
@@ -7,7 +7,7 @@
-
+
From 9a47988d73a85ea879f519732a16ba3e569665d8 Mon Sep 17 00:00:00 2001
From: Maxwell Nyamunda
<142179888+mnyamunda-scottlogic@users.noreply.github.com>
Date: Fri, 26 Jul 2024 11:53:12 +0100
Subject: [PATCH 11/24] Added logo.svg as a header in README.md at root level
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 5f9246c5ab..c95188a194 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# tgstation-server
+
 [](https://codecov.io/gh/tgstation/tgstation-server)
From a7623ce66f6d24b348433fe40aa38f2215c0ea91 Mon Sep 17 00:00:00 2001
From: Maxwell Nyamunda
<142179888+mnyamunda-scottlogic@users.noreply.github.com>
Date: Fri, 26 Jul 2024 11:59:13 +0100
Subject: [PATCH 12/24] Add centering to logo.svg
---
README.md | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index c95188a194..5fb054f1c0 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-
+
+

+
 [](https://codecov.io/gh/tgstation/tgstation-server)
From e1fe47c6e8d43722cb67c2db996c520fbc9b9dcf Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Fri, 26 Jul 2024 09:11:16 -0400
Subject: [PATCH 13/24] Remove Approval Required Label before adding it
---
.github/workflows/ci-pipeline.yml | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml
index b6a4d40c47..2e04ae0b1e 100644
--- a/.github/workflows/ci-pipeline.yml
+++ b/.github/workflows/ci-pipeline.yml
@@ -75,6 +75,12 @@ jobs:
with:
labels: CI Cleared
+ - name: "Remove 'CI Approval Required' Label"
+ if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && contains(github.event.pull_request.labels.*.name, 'CI Cleared'))
+ uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0
+ with:
+ labels: CI Approval Required
+
- name: "Add 'CI Approval Required' Label"
if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && !contains(github.event.pull_request.labels.*.name, 'CI Cleared'))
uses: actions-ecosystem/action-add-labels@bd52874380e3909a1ac983768df6976535ece7f8
@@ -82,12 +88,6 @@ jobs:
labels: CI Approval Required
github_token: ${{ github.token }}
- - name: "Remove 'CI Approval Required' Label"
- if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && contains(github.event.pull_request.labels.*.name, 'CI Cleared'))
- uses: actions-ecosystem/action-remove-labels@2ce5d41b4b6aa8503e285553f75ed56e0a40bae0
- with:
- labels: CI Approval Required
-
- name: Fail Clearance Check if PR has Unlabeled new Commits from User
if: (github.event.action == 'synchronize' || github.event.action == 'reopened') || ((github.event.action == 'opened' || github.event.action == 'labeled') && !contains(github.event.pull_request.labels.*.name, 'CI Cleared'))
run: exit 1
From d6c6994bf048c9b989a4de2959c7e022ff9c6560 Mon Sep 17 00:00:00 2001
From: Maxwell Nyamunda
<142179888+mnyamunda-scottlogic@users.noreply.github.com>
Date: Fri, 26 Jul 2024 14:19:14 +0100
Subject: [PATCH 14/24] Retrieve logo via file path as opposed to raw URL,
Convert class to paragraph element
---
README.md | 53 +++++++++++++++++++++++++++++++++--------------------
1 file changed, 33 insertions(+), 20 deletions(-)
diff --git a/README.md b/README.md
index 5fb054f1c0..7a71086ab4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
-

-
+
+
+
 [](https://codecov.io/gh/tgstation/tgstation-server)
@@ -26,7 +26,7 @@ If you're just a hobbyist server host, you can probably get away with using SQLi
_HOWEVER_
-SQLite is not a battle-ready relational database. It doesn't scale well for any use case. TGS *strongly* recommends you use one of its supported standalone databases. Setting one of these up is more involved but worth the effort.
+SQLite is not a battle-ready relational database. It doesn't scale well for any use case. TGS _strongly_ recommends you use one of its supported standalone databases. Setting one of these up is more involved but worth the effort.
The supported standalone databases are:
@@ -36,8 +36,9 @@ The supported standalone databases are:
- MySQL
TGS will require either:
+
- No pre-existing database WITH schema creation permissions.
-or
+ or
- Exclusive access to a database schema that TGS has full control over.
### Installation
@@ -61,11 +62,13 @@ Note: If you use the `/silent` or `/passive` arguments to the installer, you wil
[winget](https://github.com/microsoft/winget-cli) installed is the easiest way to install the latest version of tgstation-server (provided Microsoft has approved the most recent package manifest).
Check if you have `winget` by running the following command.
+
```
winget --version
```
If it returns an error that means you don't have winget. You can easily install it by running the following commands in an administrative Windows Powershell instance:
+
```
Import-Module Appx
Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.3 -OutFile .\microsoft.ui.xaml.2.7.3.zip
@@ -156,6 +159,7 @@ Alternatively, to launch the server in the current shell, run `./tgs.sh` in the
tgstation-server supports running in a docker container. The official image repository is located at https://hub.docker.com/r/tgstation/server. It can also be built locally by running `docker build . -f build/Dockerfile -t ` in the repository root.
To create a container run
+
```sh
docker run \
-ti \ # Start with interactive terminal the first time to run the setup wizard
@@ -172,6 +176,7 @@ docker run \
-v /path/to/your/log/folder:/tgs_logs \ # Recommended, create a volume mapping for server logs
tgstation/server[:]
```
+
with any additional options you desire (i.e. You'll have to expose more game ports in order to host more than one instance).
When launching the container for the first time, you'll be prompted with the setup wizard and then the container will exit. Start the container again to launch the server.
@@ -197,14 +202,15 @@ OpenDream currently requires [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/d
How to handle a different SDK version than the ASP.NET runtime of TGS.
- On Linux, as long as OpenDream and TGS do not use the same .NET major version, you cannot achieve this with the package manager as they will conflict. For example, the 7.0 SDK can be added to an 8.0 runtime installation via the following steps.
+On Linux, as long as OpenDream and TGS do not use the same .NET major version, you cannot achieve this with the package manager as they will conflict. For example, the 7.0 SDK can be added to an 8.0 runtime installation via the following steps.
- 1. Install `tgstation-server` using any of the above methods.
- 1. [Download the Linux SDK binaries](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) for your selected architecture.
- 1. Extract everything EXCEPT the `dotnet` executable, `LICENSE.txt`, and `ThirdPartyNotices.txt` in the `.tar.gz` on top of the existing installation directory `/usr/share/dotnet/`
- 1. Run `sudo chown -R root /usr/share/dotnet`
+1. Install `tgstation-server` using any of the above methods.
+1. [Download the Linux SDK binaries](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) for your selected architecture.
+1. Extract everything EXCEPT the `dotnet` executable, `LICENSE.txt`, and `ThirdPartyNotices.txt` in the `.tar.gz` on top of the existing installation directory `/usr/share/dotnet/`
+1. Run `sudo chown -R root /usr/share/dotnet`
+
+You should now be able to run the `dotnet --list-sdks` command and see an entry for `7.0.XXX [/usr/share/dotnet/sdk]`.
- You should now be able to run the `dotnet --list-sdks` command and see an entry for `7.0.XXX [/usr/share/dotnet/sdk]`.
### Configuring
@@ -259,13 +265,14 @@ Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will
- `ControlPanel:Enable`: Enable the javascript based control panel to be served from the server via /index.html
-- `ControlPanel:AllowAnyOrigin`: Set the Access-Control-Allow-Origin header to * for all responses (also enables all headers and methods)
+- `ControlPanel:AllowAnyOrigin`: Set the Access-Control-Allow-Origin header to \* for all responses (also enables all headers and methods)
- `ControlPanel:AllowedOrigins`: Set the Access-Control-Allow-Origin headers to this list of origins for all responses (also enables all headers and methods). This is overridden by `ControlPanel:AllowAnyOrigin`
- `ControlPanel:PublicPath`: URL from which the webpanel can be accessed, defaults to "/app/". Must be an absolute path (https://example.org/path/to/webpanel) or a path starting from root (/path/to/webpanel). Note that this option does not relocate the webpanel for you; you will need a reverse proxy to relocate the webpanel
- `Elasticsearch`: tgstation-server also supports automatically ingesting its logs to ElasticSearch. You can set this up in the setup wizard, or with the following configuration:
+
```yml
Elasticsearch:
Enable: true
@@ -289,6 +296,7 @@ Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will
- `Swarm:UpdateRequiredNodeCount`: Should be set to the total number of servers in your swarm minus 1. Prevents updates from occurring unless the non-controller server count in the swarm is greater than or equal to this value.
- `Security:OAuth:`: Sets the OAuth client ID and secret for a given ``. The currently supported providers are `Keycloak`, `GitHub`, `Discord`, `InvisionCommunity` and `TGForums`. Setting these fields to `null` disables logins with the provider, but does not stop users from associating their accounts using the API. Sample Entry:
+
```yml
Security:
OAuth:
@@ -299,6 +307,7 @@ Security:
ServerUrl: "..."
UserInformationUrlOverride: "..." # For power users, leave out of configuration for most cases. Not supported by GitHub provider.
```
+
The following providers use the `RedirectUrl` setting:
- GitHub
@@ -358,6 +367,7 @@ The DMAPI is fully backwards compatible and should function with any tgstation-s
Here is a bare minimum example project that implements the essential code changes for integrating the DMAPI
Before `tgs.dm`:
+
```dm
//Remember, every codebase is different, you probably have better methods for these defines than the ones given here
#define TGS_EXTERNAL_CONFIGURATION
@@ -374,6 +384,7 @@ Before `tgs.dm`:
```
Anywhere else:
+
```dm
var/global/client_count = 0
@@ -428,11 +439,13 @@ _NOTE: Your reverse proxy setup may interfere with SSE (Server-Sent Events) whic
1. Setup a basic website configuration. Instructions on how to do so are out of scope.
2. In your Caddyfile, under a server entry, add the following (replace 5000 with the port TGS is hosted on):
+
```
https://your.site.here {
reverse_proxy localhost:5000
}
```
+
3. For this setup, your configuration's `ControlPanel:PublicPath` needs to be blank. If you have a path in `PublicPath`, it needs to be in "reverse_proxy PublicPathHere localhost:5000".
See https://caddyserver.com/docs/caddyfile/directives/reverse_proxy
@@ -442,6 +455,7 @@ See https://caddyserver.com/docs/caddyfile/directives/reverse_proxy
1. Setup a basic website configuration. Instructions on how to do so are out of scope.
2. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure NGINX to use it.
3. Setup a path under a server like the following (replace 8080 with the port TGS is hosted on):
+
```
location /tgs {
proxy_pass http://127.0.0.1:8080;
@@ -457,6 +471,7 @@ See https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/
2. Setup a basic website configuration. Instructions on how to do so are out of scope.
3. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure Apache to use it.
4. Under a VirtualHost entry, setup the following (replace 8080 with the port TGS is hosted on):
+
```
ProxyPass / http://127.0.0.1:8080
ProxyPassReverse / http://127.0.0.1:8080
@@ -465,6 +480,7 @@ ProxyPassReverse / http://127.0.0.1:8080
See https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
Example VirtualHost Entry
+
```
@@ -578,10 +594,7 @@ Bots have a set of built-in commands that can be triggered via `!tgs`, mentionin
All files in game code deployments are considered transient by default, meaning when new code is deployed, changes will be lost. Static files allow you to specify which files and folders stick around throughout all deployments.
-The `StaticFiles` folder contains 3 root folders which cannot be deleted and operate under special rules
- - `CodeModifications`
- - `EventScripts`
- - `GameStaticFiles`
+The `StaticFiles` folder contains 3 root folders which cannot be deleted and operate under special rules - `CodeModifications` - `EventScripts` - `GameStaticFiles`
These files can be modified either in host mode or system user mode. In host mode, TGS itself is responsible for reading and writing the files. In system user mode read and write actions are performed using the system account of the logged on User, enabling the use of ACLs to control access to files. Database users will not be able to use the static file system if this mode is configured for an instance.
@@ -655,12 +668,12 @@ Feel free to ask for help [on the discussions page](https://github.com/tgstation
## Contributing
-* See [CONTRIBUTING.md](.github/CONTRIBUTING.md)
+- See [CONTRIBUTING.md](.github/CONTRIBUTING.md)
## Licensing
-* The DMAPI for the project is licensed under the MIT license.
-* The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
-* The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
+- The DMAPI for the project is licensed under the MIT license.
+- The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
+- The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
See the files in the `/src/DMAPI` tree for the MIT license
From bf7494e686b508bea93cab35d34fa5e105a83148 Mon Sep 17 00:00:00 2001
From: Maxwell Nyamunda
<142179888+mnyamunda-scottlogic@users.noreply.github.com>
Date: Fri, 26 Jul 2024 15:03:13 +0100
Subject: [PATCH 15/24] revert tgstation-server header
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index 7a71086ab4..5e2326dd29 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
+# tgstation-server
+
 [](https://codecov.io/gh/tgstation/tgstation-server)
[](LICENSE) [](http://isitmaintained.com/project/tgstation/tgstation-server "Average time to resolve an issue") [](https://www.nuget.org/packages/Tgstation.Server.Api) [](https://www.nuget.org/packages/Tgstation.Server.Client)
From 91c6f5c171deb7e080a61ead4464ae43a586871f Mon Sep 17 00:00:00 2001
From: Maxwell Nyamunda
<142179888+mnyamunda-scottlogic@users.noreply.github.com>
Date: Fri, 26 Jul 2024 15:17:05 +0100
Subject: [PATCH 16/24] revert unrelated changes from dev branch rebase
---
README.md | 49 ++++++++++++++++++-------------------------------
1 file changed, 18 insertions(+), 31 deletions(-)
diff --git a/README.md b/README.md
index 5e2326dd29..610f3d75b8 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@ If you're just a hobbyist server host, you can probably get away with using SQLi
_HOWEVER_
-SQLite is not a battle-ready relational database. It doesn't scale well for any use case. TGS _strongly_ recommends you use one of its supported standalone databases. Setting one of these up is more involved but worth the effort.
+SQLite is not a battle-ready relational database. It doesn't scale well for any use case. TGS *strongly* recommends you use one of its supported standalone databases. Setting one of these up is more involved but worth the effort.
The supported standalone databases are:
@@ -38,9 +38,8 @@ The supported standalone databases are:
- MySQL
TGS will require either:
-
- No pre-existing database WITH schema creation permissions.
- or
+or
- Exclusive access to a database schema that TGS has full control over.
### Installation
@@ -64,13 +63,11 @@ Note: If you use the `/silent` or `/passive` arguments to the installer, you wil
[winget](https://github.com/microsoft/winget-cli) installed is the easiest way to install the latest version of tgstation-server (provided Microsoft has approved the most recent package manifest).
Check if you have `winget` by running the following command.
-
```
winget --version
```
If it returns an error that means you don't have winget. You can easily install it by running the following commands in an administrative Windows Powershell instance:
-
```
Import-Module Appx
Invoke-WebRequest -Uri https://www.nuget.org/api/v2/package/Microsoft.UI.Xaml/2.7.3 -OutFile .\microsoft.ui.xaml.2.7.3.zip
@@ -161,7 +158,6 @@ Alternatively, to launch the server in the current shell, run `./tgs.sh` in the
tgstation-server supports running in a docker container. The official image repository is located at https://hub.docker.com/r/tgstation/server. It can also be built locally by running `docker build . -f build/Dockerfile -t ` in the repository root.
To create a container run
-
```sh
docker run \
-ti \ # Start with interactive terminal the first time to run the setup wizard
@@ -178,7 +174,6 @@ docker run \
-v /path/to/your/log/folder:/tgs_logs \ # Recommended, create a volume mapping for server logs
tgstation/server[:]
```
-
with any additional options you desire (i.e. You'll have to expose more game ports in order to host more than one instance).
When launching the container for the first time, you'll be prompted with the setup wizard and then the container will exit. Start the container again to launch the server.
@@ -204,15 +199,14 @@ OpenDream currently requires [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/d
How to handle a different SDK version than the ASP.NET runtime of TGS.
-On Linux, as long as OpenDream and TGS do not use the same .NET major version, you cannot achieve this with the package manager as they will conflict. For example, the 7.0 SDK can be added to an 8.0 runtime installation via the following steps.
+ On Linux, as long as OpenDream and TGS do not use the same .NET major version, you cannot achieve this with the package manager as they will conflict. For example, the 7.0 SDK can be added to an 8.0 runtime installation via the following steps.
-1. Install `tgstation-server` using any of the above methods.
-1. [Download the Linux SDK binaries](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) for your selected architecture.
-1. Extract everything EXCEPT the `dotnet` executable, `LICENSE.txt`, and `ThirdPartyNotices.txt` in the `.tar.gz` on top of the existing installation directory `/usr/share/dotnet/`
-1. Run `sudo chown -R root /usr/share/dotnet`
-
-You should now be able to run the `dotnet --list-sdks` command and see an entry for `7.0.XXX [/usr/share/dotnet/sdk]`.
+ 1. Install `tgstation-server` using any of the above methods.
+ 1. [Download the Linux SDK binaries](https://dotnet.microsoft.com/en-us/download/dotnet/7.0) for your selected architecture.
+ 1. Extract everything EXCEPT the `dotnet` executable, `LICENSE.txt`, and `ThirdPartyNotices.txt` in the `.tar.gz` on top of the existing installation directory `/usr/share/dotnet/`
+ 1. Run `sudo chown -R root /usr/share/dotnet`
+ You should now be able to run the `dotnet --list-sdks` command and see an entry for `7.0.XXX [/usr/share/dotnet/sdk]`.
### Configuring
@@ -267,14 +261,13 @@ Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will
- `ControlPanel:Enable`: Enable the javascript based control panel to be served from the server via /index.html
-- `ControlPanel:AllowAnyOrigin`: Set the Access-Control-Allow-Origin header to \* for all responses (also enables all headers and methods)
+- `ControlPanel:AllowAnyOrigin`: Set the Access-Control-Allow-Origin header to * for all responses (also enables all headers and methods)
- `ControlPanel:AllowedOrigins`: Set the Access-Control-Allow-Origin headers to this list of origins for all responses (also enables all headers and methods). This is overridden by `ControlPanel:AllowAnyOrigin`
- `ControlPanel:PublicPath`: URL from which the webpanel can be accessed, defaults to "/app/". Must be an absolute path (https://example.org/path/to/webpanel) or a path starting from root (/path/to/webpanel). Note that this option does not relocate the webpanel for you; you will need a reverse proxy to relocate the webpanel
- `Elasticsearch`: tgstation-server also supports automatically ingesting its logs to ElasticSearch. You can set this up in the setup wizard, or with the following configuration:
-
```yml
Elasticsearch:
Enable: true
@@ -298,7 +291,6 @@ Create an `appsettings.Production.yml` file next to `appsettings.yml`. This will
- `Swarm:UpdateRequiredNodeCount`: Should be set to the total number of servers in your swarm minus 1. Prevents updates from occurring unless the non-controller server count in the swarm is greater than or equal to this value.
- `Security:OAuth:`: Sets the OAuth client ID and secret for a given ``. The currently supported providers are `Keycloak`, `GitHub`, `Discord`, `InvisionCommunity` and `TGForums`. Setting these fields to `null` disables logins with the provider, but does not stop users from associating their accounts using the API. Sample Entry:
-
```yml
Security:
OAuth:
@@ -309,7 +301,6 @@ Security:
ServerUrl: "..."
UserInformationUrlOverride: "..." # For power users, leave out of configuration for most cases. Not supported by GitHub provider.
```
-
The following providers use the `RedirectUrl` setting:
- GitHub
@@ -369,7 +360,6 @@ The DMAPI is fully backwards compatible and should function with any tgstation-s
Here is a bare minimum example project that implements the essential code changes for integrating the DMAPI
Before `tgs.dm`:
-
```dm
//Remember, every codebase is different, you probably have better methods for these defines than the ones given here
#define TGS_EXTERNAL_CONFIGURATION
@@ -386,7 +376,6 @@ Before `tgs.dm`:
```
Anywhere else:
-
```dm
var/global/client_count = 0
@@ -441,13 +430,11 @@ _NOTE: Your reverse proxy setup may interfere with SSE (Server-Sent Events) whic
1. Setup a basic website configuration. Instructions on how to do so are out of scope.
2. In your Caddyfile, under a server entry, add the following (replace 5000 with the port TGS is hosted on):
-
```
https://your.site.here {
reverse_proxy localhost:5000
}
```
-
3. For this setup, your configuration's `ControlPanel:PublicPath` needs to be blank. If you have a path in `PublicPath`, it needs to be in "reverse_proxy PublicPathHere localhost:5000".
See https://caddyserver.com/docs/caddyfile/directives/reverse_proxy
@@ -457,7 +444,6 @@ See https://caddyserver.com/docs/caddyfile/directives/reverse_proxy
1. Setup a basic website configuration. Instructions on how to do so are out of scope.
2. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure NGINX to use it.
3. Setup a path under a server like the following (replace 8080 with the port TGS is hosted on):
-
```
location /tgs {
proxy_pass http://127.0.0.1:8080;
@@ -473,7 +459,6 @@ See https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/
2. Setup a basic website configuration. Instructions on how to do so are out of scope.
3. Acquire an HTTPS certificate, likely via Let's Encrypt, and configure Apache to use it.
4. Under a VirtualHost entry, setup the following (replace 8080 with the port TGS is hosted on):
-
```
ProxyPass / http://127.0.0.1:8080
ProxyPassReverse / http://127.0.0.1:8080
@@ -482,7 +467,6 @@ ProxyPassReverse / http://127.0.0.1:8080
See https://httpd.apache.org/docs/2.4/howto/reverse_proxy.html
Example VirtualHost Entry
-
```
@@ -596,7 +580,10 @@ Bots have a set of built-in commands that can be triggered via `!tgs`, mentionin
All files in game code deployments are considered transient by default, meaning when new code is deployed, changes will be lost. Static files allow you to specify which files and folders stick around throughout all deployments.
-The `StaticFiles` folder contains 3 root folders which cannot be deleted and operate under special rules - `CodeModifications` - `EventScripts` - `GameStaticFiles`
+The `StaticFiles` folder contains 3 root folders which cannot be deleted and operate under special rules
+ - `CodeModifications`
+ - `EventScripts`
+ - `GameStaticFiles`
These files can be modified either in host mode or system user mode. In host mode, TGS itself is responsible for reading and writing the files. In system user mode read and write actions are performed using the system account of the logged on User, enabling the use of ACLs to control access to files. Database users will not be able to use the static file system if this mode is configured for an instance.
@@ -670,12 +657,12 @@ Feel free to ask for help [on the discussions page](https://github.com/tgstation
## Contributing
-- See [CONTRIBUTING.md](.github/CONTRIBUTING.md)
+* See [CONTRIBUTING.md](.github/CONTRIBUTING.md)
## Licensing
-- The DMAPI for the project is licensed under the MIT license.
-- The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
-- The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
+* The DMAPI for the project is licensed under the MIT license.
+* The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
+* The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
-See the files in the `/src/DMAPI` tree for the MIT license
+See the files in the `/src/DMAPI` tree for the MIT license
\ No newline at end of file
From e0ebf3b14435adb4715d8a35b547b7304c83a9aa Mon Sep 17 00:00:00 2001
From: Maxwell Nyamunda
<142179888+mnyamunda-scottlogic@users.noreply.github.com>
Date: Fri, 26 Jul 2024 15:39:47 +0100
Subject: [PATCH 17/24] Revert blank final line to follow good practice
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 610f3d75b8..794543dc85 100644
--- a/README.md
+++ b/README.md
@@ -665,4 +665,4 @@ Feel free to ask for help [on the discussions page](https://github.com/tgstation
* The /tg/station 13 icon is licensed under [Creative Commons 3.0 BY-SA](http://creativecommons.org/licenses/by-sa/3.0/).
* The remainder of the project is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html)
-See the files in the `/src/DMAPI` tree for the MIT license
\ No newline at end of file
+See the files in the `/src/DMAPI` tree for the MIT license
From 17712f682d0c8c2145de6b37d4cb15a23149dcdd Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Fri, 26 Jul 2024 11:12:07 -0400
Subject: [PATCH 18/24] Add `IPlatformIdentifier.NormalizePath`
---
.../System/IPlatformIdentifier.cs | 7 +++++++
.../System/PlatformIdentifier.cs | 13 ++++++++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs b/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs
index 805b93b97d..b22173d2c6 100644
--- a/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs
+++ b/src/Tgstation.Server.Host/System/IPlatformIdentifier.cs
@@ -17,5 +17,12 @@ namespace Tgstation.Server.Host.System
/// The extension of executable script files for the system.
///
string ScriptFileExtension { get; }
+
+ ///
+ /// Normalize a path for consistency.
+ ///
+ /// The path to normalize.
+ /// The normalized path.
+ string NormalizePath(string path);
}
}
diff --git a/src/Tgstation.Server.Host/System/PlatformIdentifier.cs b/src/Tgstation.Server.Host/System/PlatformIdentifier.cs
index 22931b97ce..afa9d607e1 100644
--- a/src/Tgstation.Server.Host/System/PlatformIdentifier.cs
+++ b/src/Tgstation.Server.Host/System/PlatformIdentifier.cs
@@ -1,4 +1,5 @@
-using System.Runtime.InteropServices;
+using System;
+using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Tgstation.Server.Host.System
@@ -21,5 +22,15 @@ namespace Tgstation.Server.Host.System
IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
ScriptFileExtension = IsWindows ? "bat" : "sh";
}
+
+ ///
+ public string NormalizePath(string path)
+ {
+ ArgumentNullException.ThrowIfNull(path);
+ if (IsWindows)
+ path = path.Replace('\\', '/');
+
+ return path;
+ }
}
}
From dfbc6abc07e05755b4ef7b97ec52e13b91dde792 Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 20:47:31 -0400
Subject: [PATCH 19/24] Make `InstanceController` use new helper function
---
.../Controllers/InstanceController.cs | 106 +++++++-----------
1 file changed, 40 insertions(+), 66 deletions(-)
diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs
index 85f37f0f84..b56d5a9a7a 100644
--- a/src/Tgstation.Server.Host/Controllers/InstanceController.cs
+++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs
@@ -103,8 +103,8 @@ namespace Tgstation.Server.Host.Controllers
IInstanceManager instanceManager,
IJobManager jobManager,
IIOManager ioManager,
- IPortAllocator portAllocator,
IPlatformIdentifier platformIdentifier,
+ IPortAllocator portAllocator,
IPermissionsUpdateNotifyee permissionsUpdateNotifyee,
IOptions generalConfigurationOptions,
IOptions swarmConfigurationOptions,
@@ -150,77 +150,52 @@ namespace Tgstation.Server.Host.Controllers
if (earlyOut != null)
return earlyOut;
- var unNormalizedPath = model.Path;
- var targetInstancePath = NormalizePath(unNormalizedPath);
+ var targetInstancePath = NormalizePath(model.Path!);
model.Path = targetInstancePath;
- var installationDirectoryPath = NormalizePath(DefaultIOManager.CurrentDirectory);
-
- bool InstanceIsChildOf(string otherPath)
- {
- if (!targetInstancePath.StartsWith(otherPath, StringComparison.Ordinal))
- return false;
-
- bool sameLength = targetInstancePath.Length == otherPath.Length;
- char dirSeparatorChar = targetInstancePath.ToCharArray()[Math.Min(otherPath.Length, targetInstancePath.Length - 1)];
- return sameLength
- || dirSeparatorChar == Path.DirectorySeparatorChar
- || dirSeparatorChar == Path.AltDirectorySeparatorChar;
- }
-
- if (InstanceIsChildOf(installationDirectoryPath))
+ var installationDirectoryPath = DefaultIOManager.CurrentDirectory;
+ if (await ioManager.PathIsChildOf(installationDirectoryPath, targetInstancePath, cancellationToken))
return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtConflictingPath));
// Validate it's not a child of any other instance
- ulong countOfOtherInstances = 0;
- using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken))
- {
- var newCancellationToken = cts.Token;
- try
+ var instancePaths = await DatabaseContext
+ .Instances
+ .AsQueryable()
+ .Where(x => x.SwarmIdentifer == swarmConfiguration.Identifier)
+ .Select(x => new Models.Instance
{
- await DatabaseContext
- .Instances
- .AsQueryable()
- .Where(x => x.SwarmIdentifer == swarmConfiguration.Identifier)
- .Select(x => new Models.Instance
- {
- Path = x.Path,
- })
- .ForEachAsync(
- otherInstance =>
- {
- if (++countOfOtherInstances >= generalConfiguration.InstanceLimit)
- earlyOut ??= Conflict(new ErrorMessageResponse(ErrorCode.InstanceLimitReached));
- else if (InstanceIsChildOf(otherInstance.Path!))
- earlyOut ??= Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtConflictingPath));
+ Path = x.Path,
+ })
+ .ToListAsync(cancellationToken);
- if (earlyOut != null && !newCancellationToken.IsCancellationRequested)
- cts.Cancel();
- },
- newCancellationToken);
- }
- catch (OperationCanceledException)
- {
- cancellationToken.ThrowIfCancellationRequested();
- }
- }
+ if ((instancePaths.Count + 1) >= generalConfiguration.InstanceLimit)
+ return Conflict(new ErrorMessageResponse(ErrorCode.InstanceLimitReached));
- if (earlyOut != null)
- return earlyOut;
+ var instancePathChecks = instancePaths
+ .Select(otherInstance => ioManager.PathIsChildOf(otherInstance.Path!, targetInstancePath, cancellationToken))
+ .ToArray();
+
+ await Task.WhenAll(instancePathChecks);
+
+ if (instancePathChecks.Any(task => task.Result))
+ return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtConflictingPath));
// Last test, ensure it's in the list of valid paths
- if (!(generalConfiguration.ValidInstancePaths?
- .Select(path => NormalizePath(path))
- .Any(path => InstanceIsChildOf(path)) ?? true))
+ var pathChecks = generalConfiguration.ValidInstancePaths?
+ .Select(path => ioManager.PathIsChildOf(path, targetInstancePath, cancellationToken))
+ .ToArray()
+ ?? Enumerable.Empty>();
+ await Task.WhenAll(pathChecks);
+ if (!pathChecks.All(task => task.Result))
return BadRequest(new ErrorMessageResponse(ErrorCode.InstanceNotAtWhitelistedPath));
async ValueTask DirExistsAndIsNotEmpty()
{
- if (!await ioManager.DirectoryExists(model.Path, cancellationToken))
+ if (!await ioManager.DirectoryExists(targetInstancePath, cancellationToken))
return false;
- var filesTask = ioManager.GetFiles(model.Path, cancellationToken);
- var dirsTask = ioManager.GetDirectories(model.Path, cancellationToken);
+ var filesTask = ioManager.GetFiles(targetInstancePath, cancellationToken);
+ var dirsTask = ioManager.GetDirectories(targetInstancePath, cancellationToken);
var files = await filesTask;
var dirs = await dirsTask;
@@ -230,8 +205,8 @@ namespace Tgstation.Server.Host.Controllers
var dirExistsTask = DirExistsAndIsNotEmpty();
bool attached = false;
- if (await ioManager.FileExists(model.Path, cancellationToken) || await dirExistsTask)
- if (!await ioManager.FileExists(ioManager.ConcatPath(model.Path, InstanceAttachFileName), cancellationToken))
+ if (await ioManager.FileExists(targetInstancePath, cancellationToken) || await dirExistsTask)
+ if (!await ioManager.FileExists(ioManager.ConcatPath(targetInstancePath, InstanceAttachFileName), cancellationToken))
return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtExistingPath));
else
attached = true;
@@ -248,7 +223,7 @@ namespace Tgstation.Server.Host.Controllers
try
{
// actually reserve it now
- await ioManager.CreateDirectory(unNormalizedPath, cancellationToken);
+ await ioManager.CreateDirectory(targetInstancePath, cancellationToken);
await ioManager.DeleteFile(ioManager.ConcatPath(targetInstancePath, InstanceAttachFileName), cancellationToken);
}
catch
@@ -397,13 +372,13 @@ namespace Tgstation.Server.Host.Controllers
}
string? originalModelPath = null;
- string? rawPath = null;
+ string? normalizedPath = null;
var originalOnline = originalModel.Online!.Value;
if (model.Path != null)
{
- rawPath = NormalizePath(model.Path);
+ normalizedPath = NormalizePath(model.Path);
- if (rawPath != originalModel.Path)
+ if (normalizedPath != originalModel.Path)
{
if (!userRights.HasFlag(InstanceManagerRights.Relocate))
return Forbid();
@@ -415,7 +390,7 @@ namespace Tgstation.Server.Host.Controllers
return Conflict(new ErrorMessageResponse(ErrorCode.InstanceAtExistingPath));
originalModelPath = originalModel.Path;
- originalModel.Path = rawPath;
+ originalModel.Path = normalizedPath;
}
}
@@ -505,7 +480,7 @@ namespace Tgstation.Server.Host.Controllers
var moving = originalModelPath != null;
if (moving)
{
- var description = $"Move instance ID {originalModel.Id} from {originalModelPath} to {rawPath}";
+ var description = $"Move instance ID {originalModel.Id} from {originalModelPath} to {normalizedPath}";
var job = Job.Create(JobCode.Move, AuthenticationContext.User, originalModel, InstanceManagerRights.Relocate);
job.Description = description;
@@ -823,8 +798,7 @@ namespace Tgstation.Server.Host.Controllers
return null;
path = ioManager.ResolvePath(path);
- if (platformIdentifier.IsWindows)
- path = path.ToUpperInvariant().Replace('\\', '/');
+ path = platformIdentifier.NormalizePath(path);
return path;
}
From d11dc015370f4e36576c40bad97c1351eda0a391 Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 20:47:35 -0400
Subject: [PATCH 20/24] New error code for if you try to set a bad project name
---
build/Version.props | 6 ++---
src/Tgstation.Server.Api/Models/ErrorCode.cs | 6 +++++
.../Components/Deployment/DreamMaker.cs | 3 +++
.../Live/Instance/DeploymentTest.cs | 23 +++++++++++++++++++
4 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/build/Version.props b/build/Version.props
index 80aeac2c88..0125973c28 100644
--- a/build/Version.props
+++ b/build/Version.props
@@ -5,10 +5,10 @@
6.7.0
5.1.0
- 10.5.0
+ 10.6.0
7.0.0
- 13.5.0
- 15.5.0
+ 13.6.0
+ 15.6.0
7.1.3
5.9.0
1.4.1
diff --git a/src/Tgstation.Server.Api/Models/ErrorCode.cs b/src/Tgstation.Server.Api/Models/ErrorCode.cs
index 7dc134f905..9e1fea2fe9 100644
--- a/src/Tgstation.Server.Api/Models/ErrorCode.cs
+++ b/src/Tgstation.Server.Api/Models/ErrorCode.cs
@@ -651,5 +651,11 @@ namespace Tgstation.Server.Api.Models
///
[Description("Could not create dump as dotnet diagnostics threw an exception!")]
DotnetDiagnosticsFailure,
+
+ ///
+ /// The configured .dme could not be found.
+ ///
+ [Description("Could not load configured .dme due to it being outside the deployment directory! This should be a relative path.")]
+ DeploymentWrongDme,
}
}
diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
index 6890dc25e5..2ae26d48c8 100644
--- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
@@ -610,6 +610,9 @@ namespace Tgstation.Server.Host.Components.Deployment
var targetDmeExists = await ioManager.FileExists(targetDme, cancellationToken);
if (!targetDmeExists)
throw new JobException(ErrorCode.DeploymentMissingDme);
+
+ if (!await ioManager.PathIsChildOf(outputDirectory, targetDme, cancellationToken))
+ throw new JobException(ErrorCode.DeploymentWrongDme);
}
logger.LogDebug("Selected \"{dmeName}.dme\" for compilation!", job.DmeName);
diff --git a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs
index fe32e9e196..1b20cd560c 100644
--- a/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs
+++ b/tests/Tgstation.Server.Tests/Live/Instance/DeploymentTest.cs
@@ -1,4 +1,6 @@
using System;
+using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -200,6 +202,27 @@ namespace Tgstation.Server.Tests.Live.Instance
deployJob = await dreamMakerClient.Compile(cancellationToken);
await WaitForJob(deployJob, 40, true, ErrorCode.DeploymentMissingDme, cancellationToken);
+ // set to an absolute path that does exist
+ var tempFile = Path.GetTempFileName().Replace('\\', '/');
+ try
+ {
+ // for testing purposes, assume same drive for windows
+ var relativePath = $"../../{String.Join("/", instanceClient.Metadata.Path.Replace('\\', '/').Where(pathChar => pathChar == '/').Select(x => ".."))}{tempFile.Substring(tempFile.IndexOf('/'))}";
+ var dmePath = $"{tempFile}.dme";
+ File.Move(tempFile, dmePath);
+ tempFile = dmePath;
+ await dreamMakerClient.Update(new DreamMakerRequest
+ {
+ ProjectName = relativePath
+ }, cancellationToken);
+ deployJob = await dreamMakerClient.Compile(cancellationToken);
+ await WaitForJob(deployJob, 40, true, ErrorCode.DeploymentWrongDme, cancellationToken);
+ }
+ finally
+ {
+ File.Delete(tempFile);
+ }
+
// check that we can change the visibility
await vpTest;
From 0ba88f78fcd94f7455945f28efc1ce625a2a3ece Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Wed, 24 Jul 2024 20:54:59 -0400
Subject: [PATCH 21/24] Version bump to 6.8.0
---
build/Version.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build/Version.props b/build/Version.props
index 0125973c28..0099619aac 100644
--- a/build/Version.props
+++ b/build/Version.props
@@ -3,7 +3,7 @@
- 6.7.0
+ 6.8.0
5.1.0
10.6.0
7.0.0
From e8d31177639182741ccf2f944e34b88b15222c00 Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Fri, 26 Jul 2024 11:18:10 -0400
Subject: [PATCH 22/24] Make `DatabaseSeeder` use new helper function
---
.../Database/DatabaseSeeder.cs | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
index c88c7a5e0d..7e0719022e 100644
--- a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
+++ b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
@@ -223,16 +223,13 @@ namespace Tgstation.Server.Host.Database
}
}
- if (platformIdentifier.IsWindows)
- {
- // normalize backslashes to forward slashes
- var allInstances = await databaseContext
- .Instances
- .AsQueryable()
- .ToListAsync(cancellationToken);
- foreach (var instance in allInstances)
- instance.Path = instance.Path!.Replace('\\', '/');
- }
+ // normalize backslashes to forward slashes
+ var allInstances = await databaseContext
+ .Instances
+ .AsQueryable()
+ .ToListAsync(cancellationToken);
+ foreach (var instance in allInstances)
+ instance.Path = platformIdentifier.NormalizePath(instance.Path!.Replace('\\', '/'));
if (generalConfiguration.ByondTopicTimeout != 0)
{
From 6559088ff1171c4e80437a9d701f16ca2f14a035 Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Fri, 26 Jul 2024 11:20:55 -0400
Subject: [PATCH 23/24] Fix instance path normalization occuring across swarm
instances
---
src/Tgstation.Server.Host/Database/DatabaseSeeder.cs | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
index 7e0719022e..694fff0c9c 100644
--- a/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
+++ b/src/Tgstation.Server.Host/Database/DatabaseSeeder.cs
@@ -51,6 +51,11 @@ namespace Tgstation.Server.Host.Database
///
readonly DatabaseConfiguration databaseConfiguration;
+ ///
+ /// The for the .
+ ///
+ readonly SwarmConfiguration swarmConfiguration;
+
///
/// Add a default system to a given .
///
@@ -83,6 +88,7 @@ namespace Tgstation.Server.Host.Database
/// The value of .
/// The containing the value of .
/// The containing the value of .
+ /// The containing the value of .
/// The value of .
/// The value of .
public DatabaseSeeder(
@@ -90,6 +96,7 @@ namespace Tgstation.Server.Host.Database
IPlatformIdentifier platformIdentifier,
IOptions generalConfigurationOptions,
IOptions databaseConfigurationOptions,
+ IOptions swarmConfigurationOptions,
ILogger databaseLogger,
ILogger logger)
{
@@ -97,6 +104,7 @@ namespace Tgstation.Server.Host.Database
this.platformIdentifier = platformIdentifier ?? throw new ArgumentNullException(nameof(platformIdentifier));
databaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions));
generalConfiguration = generalConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(generalConfigurationOptions));
+ swarmConfiguration = swarmConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(swarmConfigurationOptions));
this.databaseLogger = databaseLogger ?? throw new ArgumentNullException(nameof(databaseLogger));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
@@ -227,6 +235,7 @@ namespace Tgstation.Server.Host.Database
var allInstances = await databaseContext
.Instances
.AsQueryable()
+ .Where(instance => instance.SwarmIdentifer == swarmConfiguration.Identifier)
.ToListAsync(cancellationToken);
foreach (var instance in allInstances)
instance.Path = platformIdentifier.NormalizePath(instance.Path!.Replace('\\', '/'));
From 3acd25e6d4c951f6c0fa8747cab9b284bd40e5cf Mon Sep 17 00:00:00 2001
From: Jordan Dominion
Date: Fri, 26 Jul 2024 11:25:52 -0400
Subject: [PATCH 24/24] Check wrong .dme before .dme exists
---
.../Components/Deployment/DreamMaker.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
index 2ae26d48c8..e923f77305 100644
--- a/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
+++ b/src/Tgstation.Server.Host/Components/Deployment/DreamMaker.cs
@@ -607,12 +607,12 @@ namespace Tgstation.Server.Host.Components.Deployment
else
{
var targetDme = ioManager.ConcatPath(outputDirectory, String.Join('.', job.DmeName, DmeExtension));
+ if (!await ioManager.PathIsChildOf(outputDirectory, targetDme, cancellationToken))
+ throw new JobException(ErrorCode.DeploymentWrongDme);
+
var targetDmeExists = await ioManager.FileExists(targetDme, cancellationToken);
if (!targetDmeExists)
throw new JobException(ErrorCode.DeploymentMissingDme);
-
- if (!await ioManager.PathIsChildOf(outputDirectory, targetDme, cancellationToken))
- throw new JobException(ErrorCode.DeploymentWrongDme);
}
logger.LogDebug("Selected \"{dmeName}.dme\" for compilation!", job.DmeName);