diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c4696228..ab33044f 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,3 +5,8 @@ updates: target-branch: "develop" schedule: interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "develop" + schedule: + interval: "daily" diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 39604b70..35eb2696 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -14,15 +14,15 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: 10.0.x - name: Cache NuGet packages - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 2170b1a7..8ca70d2e 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -2,32 +2,8 @@ name: NuGet Publish on: push: - branches: [ master, develop ] + branches: [ develop ] tags: [ '*' ] - workflow_dispatch: - inputs: - version: - description: 'Override version (optional)' - required: false - type: string - environment: - description: 'Publish environment' - required: false - default: 'auto' - type: choice - options: - - auto - - staging - - production - create_release: - description: 'Create GitHub release (production only)' - required: false - type: boolean - default: false - release_notes: - description: 'Release notes' - required: false - type: string env: DOTNET_VERSION: 10.0.x @@ -45,17 +21,17 @@ jobs: create-release: ${{ steps.setup.outputs.create-release }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Cache NuGet packages - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} @@ -134,11 +110,6 @@ jobs: environment = 'production'; // Create GitHub release for all tags createRelease = true; - } else if (ref === 'refs/heads/master') { - // master branch builds (preview versions) - version = `${baseVersion}-preview-${context.runNumber}`; - environment = 'staging'; - shouldPublish = false; // Don't auto-publish from master } } @@ -185,7 +156,7 @@ jobs: ls -la ./artifacts/ || echo "No artifacts found" - name: Upload artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: steps.setup.outputs.should-publish == 'true' with: name: nuget-packages-${{ steps.setup.outputs.version }} @@ -202,13 +173,13 @@ jobs: steps: - name: Download artifacts - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: nuget-packages-${{ needs.build.outputs.version }} path: ./artifacts - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: dotnet-version: ${{ env.DOTNET_VERSION }} @@ -243,7 +214,7 @@ jobs: - name: Upload release assets if: needs.build.outputs.create-release == 'true' - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const fs = require('fs'); diff --git a/Netorrent.Example/Netorrent.Example.csproj b/Netorrent.Example/Netorrent.Example.csproj index 7eec74b7..b1b8ab9c 100644 --- a/Netorrent.Example/Netorrent.Example.csproj +++ b/Netorrent.Example/Netorrent.Example.csproj @@ -7,8 +7,8 @@ enable - - + + diff --git a/Netorrent.Tests.Integration/Fixtures/OpenTrackerFixture.cs b/Netorrent.Tests.Integration/Fixtures/OpenTrackerFixture.cs index 4985b450..1fa8e10b 100644 --- a/Netorrent.Tests.Integration/Fixtures/OpenTrackerFixture.cs +++ b/Netorrent.Tests.Integration/Fixtures/OpenTrackerFixture.cs @@ -14,8 +14,7 @@ public class OpenTrackerFixture : IAsyncInitializer, IAsyncDisposable public async Task InitializeAsync() { - _container = new ContainerBuilder() - .WithImage("xbank/opentracker-docker") + _container = new ContainerBuilder("xbank/opentracker-docker") .WithName("opentracker-test") .WithPortBinding("6969/tcp", true) .WithPortBinding("6969/udp", true) diff --git a/Netorrent.Tests.Integration/Netorrent.Tests.Integration.csproj b/Netorrent.Tests.Integration/Netorrent.Tests.Integration.csproj index e182a278..324ffe13 100644 --- a/Netorrent.Tests.Integration/Netorrent.Tests.Integration.csproj +++ b/Netorrent.Tests.Integration/Netorrent.Tests.Integration.csproj @@ -7,7 +7,7 @@ true - + diff --git a/Netorrent.Tests/Netorrent.Tests.csproj b/Netorrent.Tests/Netorrent.Tests.csproj index cfb17590..77b9f44d 100644 --- a/Netorrent.Tests/Netorrent.Tests.csproj +++ b/Netorrent.Tests/Netorrent.Tests.csproj @@ -11,12 +11,12 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Netorrent/IO/MessageStream.cs b/Netorrent/IO/MessageStream.cs index 6f18dea7..f7460839 100644 --- a/Netorrent/IO/MessageStream.cs +++ b/Netorrent/IO/MessageStream.cs @@ -9,10 +9,10 @@ namespace Netorrent.IO; internal class MessageStream(Stream stream, Handshake handshake, TimeSpan timeout) : IMessageStream { private readonly Channel _incomingMessages = Channel.CreateBounded( - new BoundedChannelOptions(512) { SingleWriter = true, SingleReader = true } + new BoundedChannelOptions(128) { SingleWriter = true, SingleReader = true } ); private readonly Channel _outgoingMessages = Channel.CreateBounded( - new BoundedChannelOptions(512) { SingleWriter = false, SingleReader = true } + new BoundedChannelOptions(128) { SingleWriter = false, SingleReader = true } ); public Handshake Handshake => handshake; diff --git a/Netorrent/Netorrent.csproj b/Netorrent/Netorrent.csproj index 42685cd2..d4b6206c 100644 --- a/Netorrent/Netorrent.csproj +++ b/Netorrent/Netorrent.csproj @@ -25,11 +25,11 @@ CS8600;CS8602;CS8603;CS8604;CS8618;CS8625 - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Netorrent/P2P/Download/RequestScheduler.cs b/Netorrent/P2P/Download/RequestScheduler.cs index cdc384aa..d032d7d6 100644 --- a/Netorrent/P2P/Download/RequestScheduler.cs +++ b/Netorrent/P2P/Download/RequestScheduler.cs @@ -22,7 +22,7 @@ ILogger logger private readonly Channel _downloadMessageChannel = Channel.CreateBounded( - new BoundedChannelOptions(512) { SingleWriter = false, SingleReader = true } + new BoundedChannelOptions(128) { SingleWriter = false, SingleReader = true } ); private static readonly DownloadMessage.CheckTimeoutMessage _timeoutMessage = new(); diff --git a/Netorrent/P2P/PeerConnection.cs b/Netorrent/P2P/PeerConnection.cs index 0eaf2122..fb4c6136 100644 --- a/Netorrent/P2P/PeerConnection.cs +++ b/Netorrent/P2P/PeerConnection.cs @@ -167,13 +167,13 @@ private async Task RunAsync(CancellationTokenSource cancellationTokenSource) await SendBitfieldAsync(MyBitField, cancellationTokenSource.Token).ConfigureAwait(false); await using var downloadTimer = DownloadTracker - .StartSampling(500.Milliseconds) + .StartSampling(100.Milliseconds) .ConfigureAwait(false); await using var uploadTimer = UploadTracker - .StartSampling(500.Milliseconds) + .StartSampling(100.Milliseconds) .ConfigureAwait(false); await using var requestWindowTimer = PeerRequestWindow - .StartSampling(500.Milliseconds, DownloadTracker) + .StartSampling(100.Milliseconds, DownloadTracker) .ConfigureAwait(false); try diff --git a/Netorrent/P2P/PeersClient.cs b/Netorrent/P2P/PeersClient.cs index d8631544..c6f43bbe 100644 --- a/Netorrent/P2P/PeersClient.cs +++ b/Netorrent/P2P/PeersClient.cs @@ -27,7 +27,7 @@ ILogger logger private readonly Subject _peerConnected = new(); private readonly Channel _peerConnections = Channel.CreateBounded( - new BoundedChannelOptions(100) { SingleReader = true, SingleWriter = false } + new BoundedChannelOptions(128) { SingleReader = true, SingleWriter = false } ); public PeerId PeerId => peerId; diff --git a/Netorrent/P2P/Upload/UploadScheduler.cs b/Netorrent/P2P/Upload/UploadScheduler.cs index 13f7561e..53dbe5eb 100644 --- a/Netorrent/P2P/Upload/UploadScheduler.cs +++ b/Netorrent/P2P/Upload/UploadScheduler.cs @@ -23,7 +23,7 @@ ILogger logger private readonly Channel _uploadMessagesChannel = Channel.CreateBounded( - new BoundedChannelOptions(256) { SingleWriter = false, SingleReader = true } + new BoundedChannelOptions(128) { SingleWriter = false, SingleReader = true } ); private static readonly UploadMessage.CheckRoundMessage _checkRoundMessage = new(); diff --git a/Netorrent/TorrentFile/Torrent.cs b/Netorrent/TorrentFile/Torrent.cs index 7eb1d84b..27914151 100644 --- a/Netorrent/TorrentFile/Torrent.cs +++ b/Netorrent/TorrentFile/Torrent.cs @@ -54,7 +54,7 @@ IReadOnlySet downloadedPieces var files = metaInfo.Info.NormalizedFiles; var totalSize = files.Sum(i => i.Length); var trackersChannel = Channel.CreateBounded( - new BoundedChannelOptions(100) { SingleWriter = false, SingleReader = true } + new BoundedChannelOptions(128) { SingleWriter = false, SingleReader = true } ); MetaInfo = metaInfo; diff --git a/README.md b/README.md index ecae42b4..518df0c9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Netorrent -A high-performance, async-first .NET 10.0 BitTorrent client library for downloading and seeding torrents. +A async-first .NET 10.0 BitTorrent client library for downloading and seeding torrents. ## Installation @@ -56,9 +56,10 @@ await using var client = new TorrentClient(); await using var torrent = await client.LoadTorrentAsync("file.torrent", "output"); await torrent.StartAsync(); +var completionTask = torrent.Completion.AsTask(); // Monitor detailed statistics -while (!torrent.Completion.IsCompleted) +while (!completionTask.IsCompleted) { var data = torrent.Statistics.Data; var peers = torrent.Statistics.Peers; @@ -72,6 +73,8 @@ while (!torrent.Completion.IsCompleted) await Task.Delay(1000); } + +await completionTask; ``` ### Advanced Configuration @@ -160,40 +163,6 @@ The library is designed with async-first architecture for optimal performance: - **Installation**: `dotnet add package Netorrent --version 1.0.0-nightly-*` - **Warning**: May contain breaking changes or bugs -### 🚀 Preview Releases -- **Purpose**: Pre-release testing of upcoming features -- **Versioning**: Pre-release suffix (e.g., `1.0.0-preview-123`) -- **Updates**: Periodic builds from master branch -- **Installation**: `dotnet add package Netorrent --prerelease` - -### Version Selection Strategies - -**For Production Applications:** -```xml - -``` - -**For Testing Latest Features:** -```xml - -``` - -**For Early Adopters:** -```xml - -``` - -## CI/CD Integration - -The project uses GitHub Actions for automated publishing: - -- **Per-commit nightly builds** from `develop` branch -- **Release builds** from git tags -- **Manual publishing** for custom versions -- **Comprehensive testing** before all publications - -See [`.github/workflows/`](.github/workflows/) for complete workflow configurations. - ## Features - [x] **Torrent files** - Complete .torrent file support diff --git a/docs/NuGet-Publishing.md b/docs/NuGet-Publishing.md deleted file mode 100644 index 75d5aa71..00000000 --- a/docs/NuGet-Publishing.md +++ /dev/null @@ -1,289 +0,0 @@ -# NuGet Publishing Setup Guide - -This guide explains how the Netorrent library uses GitHub Actions to publish packages to NuGet with both nightly and stable releases. - -## Overview - -The project implements a dual-feed publishing strategy: -- **Nightly builds**: Per-commit packages from `develop` branch -- **Stable releases**: Versioned packages from git tags - -## Required Setup - -### 1. GitHub Secrets - -Add these secrets to your GitHub repository: - -#### `NUGET_API_KEY` -- **Purpose**: Authenticate with NuGet.org for publishing -- **How to get**: - 1. Go to [nuget.org](https://www.nuget.org/) - 2. Sign in with your Microsoft account - 3. Go to Account > API Keys - 4. Create a new API key with `Push` scope - 5. Set the glob pattern to `Netorrent.*` (or leave blank for all packages) - 6. Copy the generated key - -### 2. Environment Protection Rules - -Configure these environments in GitHub repository settings: - -#### `nightly` Environment -- **Purpose**: Protect nightly package publishing -- **Protection rules**: - - No reviewers required (for rapid deployment) - - Wait timer: 0 minutes - -#### `production` Environment -- **Purpose**: Protect stable release publishing -- **Protection rules**: - - Require reviewers (1-2 reviewers recommended) - - Wait timer: 5 minutes (optional) - - Prevent self-approval - -## Workflow Triggers - -### Automatic Triggers - -#### Nightly Builds (Per-Commit) -- **Trigger**: Every push to `develop` branch -- **Version**: `1.0.0-nightly-YYYYMMDD-HHMM-commit` -- **Package Type**: Pre-release -- **Symbol Packages**: No -- **Retention**: 30 days - -#### Release Builds -- **Trigger**: Git tags matching `v*` (e.g., `v1.0.0`, `v1.1.0`) -- **Version**: From git tag -- **Package Type**: Stable -- **Symbol Packages**: Yes -- **GitHub Release**: Created automatically - -### Manual Triggers - -#### Manual Publishing Workflow -- **Trigger**: Workflow dispatch from GitHub Actions tab -- **Options**: - - Custom version specification - - Environment selection (staging/production) - - Optional GitHub release creation - - Custom release notes - -## Version Strategy - -### Nightly Version Format -``` -1.0.0-nightly-20250114-1430-a1b2c3d -│ │ │ │ │ -│ │ │ │ └─ Short commit SHA -│ │ │ └─────── Timestamp (HHMM) -│ │ └───────────── Date (YYYYMMDD) -│ └───────────────────── Base version -└───────────────────────── Nightly identifier -``` - -### Release Version Format -``` -1.2.3 -│ │ │ -│ │ └─ Patch: Bug fixes -│ └─── Minor: New features (backward compatible) -└───── Major: Breaking changes -``` - -## Configuration Files - -### Main Workflow: `.github/workflows/nuget-publish.yml` - -Key features: -- Smart version detection based on trigger type -- Comprehensive testing before publishing -- Conditional symbol package inclusion -- Artifact management with retention policies -- GitHub release automation - -### Manual Workflow: `.github/workflows/manual-publish.yml` - -Key features: -- Version format validation -- Environment-specific publishing -- Optional GitHub release creation -- Release artifact attachment - -### CI Workflow: `.github/workflows/dotnet.yml` - -Updated to avoid conflicts with publishing workflows: -- Limited to main and develop branches -- Added package caching -- Code coverage collection -- Artifact upload for debugging - -## Publishing Process - -### 1. Development Flow - -```bash -# Create feature branch -git checkout -b feature/new-feature -# Make changes... -git commit -m "Add new feature" -git push origin feature/new-feature -# Create PR to develop -# Merge to develop -# → Automatic nightly build triggered -``` - -### 2. Release Flow - -```bash -# Ensure develop is stable -git checkout develop -git pull origin develop - -# Create release branch -git checkout -b release/v1.0.0 - -# Update version in project file if needed -# Commit version changes -git commit -m "Bump version to 1.0.0" -git push origin release/v1.0.0 - -# Merge to main -git checkout main -git merge release/v1.0.0 -git push origin main - -# Create and push tag -git tag v1.0.0 -git push origin v1.0.0 -# → Automatic release build triggered -``` - -### 3. Manual Publishing - -1. Go to GitHub Actions tab -2. Select "Manual NuGet Publish" workflow -3. Click "Run workflow" -4. Fill in parameters: - - Version: `1.0.0`, `1.0.0-preview-123`, or `1.0.0-nightly-custom` - - Environment: `staging` or `production` - - Create Release: Optional for production releases -5. Click "Run workflow" - -## Consumer Usage - -### Stable Installation -```xml - -``` - -### Range Installation -```xml - -``` - -### Nightly Installation -```xml - -``` - -### Pre-release Installation -```bash -dotnet add package Netorrent --prerelease -``` - -## Troubleshooting - -### Common Issues - -#### 1. Package Already Exists -- **Cause**: Version already published -- **Solution**: Use `--skip-duplicate` flag (already configured) - -#### 2. API Key Invalid -- **Cause**: Expired or incorrect API key -- **Solution**: Regenerate API key and update GitHub secrets - -#### 3. Version Format Error -- **Cause**: Invalid semantic version format -- **Solution**: Ensure version follows `X.Y.Z[-suffix]` format - -#### 4. Tests Failed -- **Cause**: Breaking changes introduced -- **Solution**: Fix test failures before publishing - -### Debugging - -#### Workflow Logs -- Check GitHub Actions workflow runs -- Review build and test logs -- Verify package creation steps - -#### Package Verification -```bash -# Download and inspect package -dotnet nuget push --source ./local-feed package.nupkg - -# Verify package contents -dotnet nuget locals --list all -``` - -## Best Practices - -### 1. Version Management -- Follow semantic versioning -- Update version numbers for breaking changes -- Use consistent version formatting - -### 2. Testing -- Ensure all tests pass before publishing -- Maintain code coverage -- Test package installation in clean environment - -### 3. Release Notes -- Provide meaningful release notes -- Document breaking changes -- Include migration guides for major versions - -### 4. Security -- Keep API keys secure -- Use environment protection rules -- Regularly rotate API keys - -### 5. Monitoring -- Monitor package download statistics -- Set up alerts for publishing failures -- Track consumer feedback - -## Advanced Configuration - -### Multiple Package Feeds - -For organizations requiring separate feeds: - -```yaml -# Custom feed configuration -NIGHTLY_FEED: https://api.nuget.org/v3/index.json -STABLE_FEED: https://api.nuget.org/v3/index.json -PRIVATE_FEED: https://pkgs.dev.azure.com/yourorg/_packaging/yourfeed/nuget/v3/index.json -``` - -### Conditional Publishing - -```yaml -# Only publish on specific conditions -- if: github.actor == 'dependabot[bot]' - run: echo "Skipping publishing for dependabot updates" -``` - -### Package Validation - -```yaml -# Additional validation steps -- name: Validate package - run: | - dotnet nuget verify artifacts/*.nupkg - dotnet nuget verify artifacts/*.snupkg -``` - -This setup provides a robust, automated NuGet publishing pipeline that supports both rapid development cycles through nightly builds and stable releases for production use. \ No newline at end of file