diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 00000000..6e4b721d
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,165 @@
+name: Build
+
+on:
+ - push
+ - pull_request
+ - workflow_dispatch
+
+jobs:
+ Linux:
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # needed for Nerdbank.GitVersioning
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 8.0.x
+
+ - name: Build Unit Tests .NET
+ run: dotnet build -f net8.0 test/Renci.SshNet.Tests/
+
+ - name: Build IntegrationTests .NET
+ run: dotnet build -f net8.0 test/Renci.SshNet.IntegrationTests/
+
+ - name: Build IntegrationTests .NET Framework
+ run: dotnet build -f net48 test/Renci.SshNet.IntegrationTests/
+
+ - name: Run Unit Tests .NET
+ run: |
+ dotnet test \
+ -f net8.0 \
+ --no-build \
+ --logger "console;verbosity=normal" \
+ --logger GitHubActions \
+ -p:CollectCoverage=true \
+ -p:CoverletOutputFormat=cobertura \
+ -p:CoverletOutput=../../coverlet/linux_unit_test_net_8_coverage.xml \
+ test/Renci.SshNet.Tests/
+
+ - name: Run Integration Tests .NET
+ run: |
+ dotnet test \
+ -f net8.0 \
+ --no-build \
+ --logger "console;verbosity=normal" \
+ --logger GitHubActions \
+ -p:CollectCoverage=true \
+ -p:CoverletOutputFormat=cobertura \
+ -p:CoverletOutput=../../coverlet/linux_integration_test_net_8_coverage.xml \
+ test/Renci.SshNet.IntegrationTests/
+
+ # Also run a subset of the integration tests targeting netfx using mono. This is a temporary measure to get
+ # some coverage until a proper solution for running the .NET Framework integration tests in CI is found.
+ # Running all the tests causes problems which are not worth solving in this rare configuration.
+ # See https://github.com/sshnet/SSH.NET/pull/1462 and related links
+ - name: Run Integration Tests Mono
+ run: |
+ sudo apt-get install ca-certificates gnupg
+ sudo gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mono-official-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
+ echo "deb [signed-by=/usr/share/keyrings/mono-official-archive-keyring.gpg] https://download.mono-project.com/repo/ubuntu stable-focal main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
+ sudo apt-get update
+ sudo apt-get install mono-devel
+ dotnet test \
+ -f net48 \
+ --no-build \
+ --logger "console;verbosity=normal" \
+ --logger GitHubActions \
+ -p:CollectCoverage=true \
+ -p:CoverletOutputFormat=cobertura \
+ -p:CoverletOutput=../../coverlet/linux_integration_test_net_48_coverage.xml \
+ --filter "Name~Ecdh|Name~ECDsa|Name~Zlib|Name~Gcm" \
+ test/Renci.SshNet.IntegrationTests/
+
+ - name: Archive Coverlet Results
+ uses: actions/upload-artifact@v4
+ with:
+ name: Coverlet Results Linux
+ path: coverlet
+
+ Windows:
+ runs-on: windows-2022
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # needed for Nerdbank.GitVersioning
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: 9.0.x
+
+ - name: Build Solution
+ run: dotnet build Renci.SshNet.sln
+
+ - name: Publish AOT Compatibility Test App
+ run: dotnet publish -r win-x64 /warnaserror test/Renci.SshNet.AotCompatibilityTestApp/
+
+ - name: Create NuGet Package
+ run: dotnet pack
+
+ - name: Archive NuGet Package
+ uses: actions/upload-artifact@v4
+ with:
+ name: NuGet Package
+ path: src/Renci.SshNet/bin/Release/*.*nupkg
+
+ - name: Run Unit Tests .NET
+ run: |
+ dotnet test `
+ -f net8.0 `
+ --no-build `
+ --logger "console;verbosity=normal" `
+ --logger GitHubActions `
+ -p:CollectCoverage=true `
+ -p:CoverletOutputFormat=cobertura `
+ -p:CoverletOutput=../../coverlet/windows_unit_test_net_8_coverage.xml `
+ test/Renci.SshNet.Tests/
+
+ - name: Run Unit Tests .NET Framework
+ run: |
+ dotnet test `
+ -f net462 `
+ --no-build `
+ --logger "console;verbosity=normal" `
+ --logger GitHubActions `
+ -p:CollectCoverage=true `
+ -p:CoverletOutputFormat=cobertura `
+ -p:CoverletOutput=../../coverlet/windows_unit_test_net_4_6_2_coverage.xml `
+ test/Renci.SshNet.Tests/
+
+ - name: Archive Coverlet Results
+ uses: actions/upload-artifact@v4
+ with:
+ name: Coverlet Results Windows
+ path: coverlet
+
+ Publish:
+ runs-on: ubuntu-24.04
+ if: github.ref == 'refs/heads/develop'
+ permissions:
+ packages: write
+ needs:
+ - Windows
+ - Linux
+ steps:
+ - name: Download NuGet Package
+ uses: actions/download-artifact@v4
+ with:
+ name: NuGet Package
+
+ - name: Publish to GitHub NuGet Registry
+ run: |
+ dotnet nuget add source \
+ --username $GITHUB_ACTOR \
+ --password ${{ secrets.GITHUB_TOKEN }} \
+ --store-password-in-clear-text \
+ --name github \
+ "https://nuget.pkg.github.com/$GITHUB_REPOSITORY_OWNER/index.json"
+ dotnet nuget push "*.nupkg" \
+ --source github \
+ --api-key ${{ secrets.GITHUB_TOKEN }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 31578af8..ce029a36 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,7 +30,7 @@ Before subsequent coverage collections, delete the previous collections with `gi
## CI
-The repository makes use of continuous integration (CI) on [AppVeyor](https://ci.appveyor.com/project/drieseng/ssh-net/history) to validate builds and tests on PR branches and non-PR branches. At the time of writing, some tests can occasionally fail in CI due to a dependency on timing or a dependency on networking/socket code. If you see an existing test which is unrelated to your changes occasionally failing in CI but passing locally, you probably don't need to worry about it. If you see one of your newly-added tests failing, it is probably worth investigating why and whether it can be made more stable.
+The repository makes use of continuous integration (CI) with GitHub Actions to validate builds and tests on PR branches and non-PR branches. At the time of writing, some tests can occasionally fail in CI due to a dependency on timing or a dependency on networking/socket code. If you see an existing test which is unrelated to your changes occasionally failing in CI but passing locally, you probably don't need to worry about it. If you see one of your newly-added tests failing, it is probably worth investigating why and whether it can be made more stable.
## Good to know
diff --git a/Directory.Packages.props b/Directory.Packages.props
index cfd5c482..8aa6398e 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -4,12 +4,14 @@
false
-
-
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
diff --git a/README.md b/README.md
index 8d2b99d2..1daebcf5 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ SSH.NET is a Secure Shell (SSH-2) library for .NET, optimized for parallelism.
[](https://www.nuget.org/packages/SSH.NET)
[](https://www.nuget.org/packages/SSH.NET)
-[](https://ci.appveyor.com/api/projects/status/ih77qu6tap3o92gu/branch/develop)
+
## Key Features
@@ -177,6 +177,19 @@ Private keys in OpenSSH key format can be encrypted using one of the following c
The library has no special requirements to build, other than an up-to-date .NET SDK. See also [CONTRIBUTING.md](https://github.com/sshnet/SSH.NET/blob/develop/CONTRIBUTING.md).
+## Using Pre-Release NuGet Package
+
+If you need an unreleased bugfix or feature, you can use the Pre-Release NuGet packages from the `develop` branch which are published to the [GitHub NuGet Registry](https://github.com/sshnet/SSH.NET/pkgs/nuget/SSH.NET).
+In order to pull packages from the registry you first have to create a Personal Access Token with the `read:packages` permissions. Then add a NuGet Source for SSH.NET:
+
+Note: you may have to add `--store-password-in-clear-text` on non-Windows platforms.
+
+```
+dotnet nuget add source --name SSH.NET --username --password https://nuget.pkg.github.com/sshnet/index.json
+```
+
+Then you can add the the package as described [here](https://github.com/sshnet/SSH.NET/pkgs/nuget/SSH.NET).
+
## Supporting SSH.NET
Do you or your company rely on **SSH.NET** in your projects? If you want to encourage us to keep on going and show us that you appreciate our work, please consider becoming a [sponsor](https://github.com/sponsors/sshnet) through GitHub Sponsors.
diff --git a/Renci.SshNet.sln b/Renci.SshNet.sln
index 76a9977e..5bfc241d 100644
--- a/Renci.SshNet.sln
+++ b/Renci.SshNet.sln
@@ -10,7 +10,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
- appveyor.yml = appveyor.yml
CODEOWNERS = CODEOWNERS
CONTRIBUTING.md = CONTRIBUTING.md
Directory.Build.props = Directory.Build.props
@@ -83,6 +82,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.IntegrationBen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Renci.SshNet.AotCompatibilityTestApp", "test\Renci.SshNet.AotCompatibilityTestApp\Renci.SshNet.AotCompatibilityTestApp.csproj", "{F2E3FC50-4EF4-488C-B3D2-C45E99898D8B}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{05229079-6738-42CE-8F73-F4E182929B03}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{95F25B1A-2B14-4745-9087-43C00CD90EE0}"
+ ProjectSection(SolutionItems) = preProject
+ .github\workflows\build.yml = .github\workflows\build.yml
+ .github\workflows\docs.yml = .github\workflows\docs.yml
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -244,6 +251,8 @@ Global
{19895BAF-F946-470D-8497-7034F9F2A8A7} = {1E46D4B6-EE87-4D29-8641-0AE8CD8ED0F0}
{3572019A-3A57-4578-B5A2-6280576EB508} = {1E46D4B6-EE87-4D29-8641-0AE8CD8ED0F0}
{92E7B1B8-4C70-4138-9970-433B2FC2E3EB} = {1E46D4B6-EE87-4D29-8641-0AE8CD8ED0F0}
+ {05229079-6738-42CE-8F73-F4E182929B03} = {04E8CC26-116E-4116-9558-7ED542548E70}
+ {95F25B1A-2B14-4745-9087-43C00CD90EE0} = {05229079-6738-42CE-8F73-F4E182929B03}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BAD6019D-4AF7-4E15-99A0-8036E16FC0E5}
diff --git a/appveyor.yml b/appveyor.yml
index 75efe896..99d01900 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,65 +1,5 @@
-image:
- - Ubuntu2204
- - Visual Studio 2022
+# empty to avoid AppVeyor errors until it's disabled
+image: Ubuntu2204
-services:
- - docker
-
-for:
--
- matrix:
- only:
- - image: Ubuntu2204
-
- init:
- - git config --global core.autocrlf input
-
- before_build:
- - sh: mkdir artifacts -p
-
- build_script:
- - echo build
- - dotnet build -f net8.0 -c Debug test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
- - dotnet build -f net8.0 -c Debug test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
- - dotnet build -f net48 -c Debug test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
-
- test_script:
- - sh: echo "Run unit tests"
- - sh: dotnet test -f net8.0 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=linux_unit_test_net_8_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/linux_unit_test_net_8_coverage.xml test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
- - sh: echo "Run integration tests"
- - sh: dotnet test -f net8.0 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=linux_integration_test_net_8_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/linux_integration_test_net_8_coverage.xml test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
- # Also run a subset of the integration tests targeting netfx using mono. This is a temporary measure to get
- # some coverage until a proper solution for running the .NET Framework integration tests in CI is found.
- # Running all the tests causes problems which are not worth solving in this rare configuration.
- # See https://github.com/sshnet/SSH.NET/pull/1462 and related links
- - sh: dotnet test -f net48 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=linux_integration_test_net_48_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/linux_integration_test_net_48_coverage.xml --filter "Name~Ecdh|Name~ECDsa|Name~Zlib|Name~Gcm" test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
-
--
- matrix:
- only:
- - image: Visual Studio 2022
-
- init:
- - git config --global core.autocrlf true
-
- before_build:
- - ps: mkdir artifacts -f
-
- build_script:
- - echo build
- - dotnet build Renci.SshNet.sln -c Debug
- - dotnet publish -c Release -r win-x64 /warnaserror .\test\Renci.SshNet.AotCompatibilityTestApp\
- - dotnet pack -c Release
-
- test_script:
- - ps: echo "Run unit tests for .NET 8.0"
- - ps: dotnet test -f net8.0 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=windows_unit_test_net_8_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/windows_unit_test_net_8_coverage.xml test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
- - ps: echo "Run unit tests for .NET Framework 4.6.2"
- - ps: dotnet test -f net462 -c Debug --no-restore --no-build --results-directory artifacts --logger Appveyor --logger "console;verbosity=normal" --logger "liquid.md;LogFileName=windows_unit_test_net_4_6_2_report.md" -p:CollectCoverage=true -p:CoverletOutputFormat=cobertura -p:CoverletOutput=../../artifacts/windows_unit_test_net_4_6_2_coverage.xml test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
-
-artifacts:
- - path: artifacts
- name: TestResults
-
- - path: src/**/*.nupkg
- - path: src/**/*.snupkg
+build_script:
+ - echo done
\ No newline at end of file
diff --git a/test/Renci.SshNet.AotCompatibilityTestApp/Renci.SshNet.AotCompatibilityTestApp.csproj b/test/Renci.SshNet.AotCompatibilityTestApp/Renci.SshNet.AotCompatibilityTestApp.csproj
index bf4fdbc8..a18321da 100644
--- a/test/Renci.SshNet.AotCompatibilityTestApp/Renci.SshNet.AotCompatibilityTestApp.csproj
+++ b/test/Renci.SshNet.AotCompatibilityTestApp/Renci.SshNet.AotCompatibilityTestApp.csproj
@@ -2,7 +2,7 @@
Exe
- net8.0
+ net9.0
true
true
false
diff --git a/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj b/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
index bac5ae37..5fa390ad 100644
--- a/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
+++ b/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
@@ -14,12 +14,14 @@
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
-
-
runtime; build; native; contentfiles; analyzers; buildtransitive
all
diff --git a/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs b/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs
index 9727764f..a27db194 100644
--- a/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs
+++ b/test/Renci.SshNet.Tests/Common/AsyncSocketListener.cs
@@ -324,7 +324,7 @@ namespace Renci.SshNet.Tests.Common
}
catch (ObjectDisposedException)
{
- // TODO On .NET 7, sometimes we get ObjectDisposedException when _started but only on appveyor, locally it works
+ // TODO On .NET 7, sometimes we get ObjectDisposedException when _started but only in CI, locally it works
ConnectionDisconnected();
}
catch (SocketException ex)
diff --git a/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj b/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
index 59053ce4..df872d75 100644
--- a/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
+++ b/test/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj
@@ -9,12 +9,14 @@
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
-
-
runtime; build; native; contentfiles; analyzers; buildtransitive
all