Добавьте файлы проекта.

This commit is contained in:
FrigaT
2025-09-06 00:07:08 +03:00
parent 75b03c81b3
commit eb76069adf
18 changed files with 471 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
name: Ручное создание релиза
run-name: ${{ gitea.actor }} запустил создание релиза
on:
workflow_dispatch:
inputs:
version_type:
description: 'Тип версии'
required: true
type: choice
options:
- major
- minor
- patch
default: 'patch'
pre_release:
description: "Отметка pre-release (для не master веток всегда true)"
type: boolean
default: false
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet: [ '8.0.x' ]
name: .Net ${{ matrix.dotnet }} Release
steps:
- name: Получение исходников
uses: actions/checkout@v4
with:
fetch-depth: 0 # Полная история для тегов
- name: Автоматическое определение pre-release
id: pre-release-detector
run: |
# Определяем ветку по умолчанию (main/master)
DEFAULT_BRANCH=$(git remote show origin | grep 'HEAD branch' | awk '{print $3}')
echo "Default branch: $DEFAULT_BRANCH"
# Если текущая ветка не дефолтная - форсируем pre-release
if [ "$GITHUB_REF_NAME" != "$DEFAULT_BRANCH" ]; then
echo "Ветка не master - устанавливаем pre-release"
echo "PRE_RELEASE=true" >> $GITHUB_ENV
else
echo "Ветка master - используем pre-release из параметров"
echo "PRE_RELEASE=${{ github.event.inputs.pre_release }}" >> $GITHUB_ENV
fi
- name: Получение последней версии
id: versioning
run: |
# Получаем последний тег
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
echo "Последний тэг: $LATEST_TAG"
# Извлекаем цифры версии
VERSION="${LATEST_TAG#v}"
if [[ ! $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)(-.+)?$ ]]; then
echo "Неверный формат версии: $VERSION"
exit 1
fi
MAJOR=${BASH_REMATCH[1]}
MINOR=${BASH_REMATCH[2]}
PATCH=${BASH_REMATCH[3]}
# Увеличиваем версию
case "${{ github.event.inputs.version_type }}" in
major)
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
minor)
MINOR=$((MINOR + 1))
PATCH=0
;;
patch)
PATCH=$((PATCH + 1))
;;
esac
BASE_VERSION="$MAJOR.$MINOR.$PATCH"
# Добавим pre-release, если требуется
if ${{ env.PRE_RELEASE }}; then
TIMESTAMP=$(date -u +"%Y%m%d%H%M%S")
NEW_VERSION="$BASE_VERSION-pre.$TIMESTAMP"
else
NEW_VERSION="$BASE_VERSION"
fi
echo "Новая версия: $NEW_VERSION"
echo "VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "VERSION=$NEW_VERSION" >> $GITHUB_ENV
- name: Настройка .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet }}
- name: Установка версии RetailUpdatesBot
run: |
sed -i "s/<Version>.*<\/Version>/<Version>${{ env.VERSION }}<\/Version>/g" ./RetailUpdatesBot/RetailUpdatesBot.csproj
- name: Восстановление зависимостей
run: dotnet restore --nologo
- name: Сборка решения
run: dotnet build --no-restore --nologo
- name: Публикация
run: dotnet publish RetailUpdatesBot --configuration Release --runtime win-x64 --artifacts-path artifacts --nologo
- name: Создание ZIP архива
run: |
cd artifacts/publish/RetailUpdatesBot/release_win-x64/
zip -r ../RetailUpdatesBot-v${{ env.VERSION }}.zip *
cd ..
echo "ZIP_PATH=RetailUpdatesBot-v${{ env.VERSION }}.zip" >> $GITHUB_ENV
- name: Создание релиза
uses: https://gitea.com/actions/gitea-release-action@v1
env:
NODE_OPTIONS: '--experimental-fetch' # if nodejs < 18
with:
files: artifacts/publish/RetailUpdatesBot/RetailUpdatesBot-v${{ env.VERSION }}.zip
tag_name: v${{ env.VERSION }}
name: RetailUpdatesBot-v${{ env.VERSION }}
body: "## Что нового\n\n- Описание изменений\n- Функциональность\n- Исправления"
prerelease: ${{ env.PRE_RELEASE }}

22
Directory.Build.props Normal file
View File

@@ -0,0 +1,22 @@
<Project>
<PropertyGroup>
<!-- Общие настройки -->
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<WarningsNotAsErrors>CS1591</WarningsNotAsErrors>
<!-- Метаданные для NuGet -->
<Authors>FrigaT</Authors>
<Product>PipelineFramework</Product>
<Description>Гибкий и лёгкий фреймворк конвейеров для .NET-приложений</Description>
<PackageProjectUrl>https://github.com/MaxLabs/PipelineFramework</PackageProjectUrl>
<RepositoryUrl>https://github.com/MaxLabs/PipelineFramework</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageTags>pipeline;framework;middleware;dotnet</PackageTags>
<Version>0.0.1</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
</Project>

43
PipelineFramework.sln Normal file
View File

@@ -0,0 +1,43 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36414.22
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PipelineFramework", "src\PipelineFramework\PipelineFramework.csproj", "{4407AD88-6FFC-47CF-B06A-BC1D9E05B7E5}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{36D591C7-65C7-A0D1-1CBC-10CDE441BDC8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PipelineFramework.DI", "src\PipelineFramework.DI\PipelineFramework.DI.csproj", "{B5D47E22-BD13-458B-ACDC-2017C962AE80}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{754FC069-D67B-A9D7-50A1-8D1CA196D8F1}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4407AD88-6FFC-47CF-B06A-BC1D9E05B7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4407AD88-6FFC-47CF-B06A-BC1D9E05B7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4407AD88-6FFC-47CF-B06A-BC1D9E05B7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4407AD88-6FFC-47CF-B06A-BC1D9E05B7E5}.Release|Any CPU.Build.0 = Release|Any CPU
{B5D47E22-BD13-458B-ACDC-2017C962AE80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B5D47E22-BD13-458B-ACDC-2017C962AE80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B5D47E22-BD13-458B-ACDC-2017C962AE80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B5D47E22-BD13-458B-ACDC-2017C962AE80}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{4407AD88-6FFC-47CF-B06A-BC1D9E05B7E5} = {36D591C7-65C7-A0D1-1CBC-10CDE441BDC8}
{B5D47E22-BD13-458B-ACDC-2017C962AE80} = {36D591C7-65C7-A0D1-1CBC-10CDE441BDC8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {22A2D53A-92B8-4C82-A391-6A00DE84526C}
EndGlobalSection
EndGlobal

55
README.md Normal file
View File

@@ -0,0 +1,55 @@
# PipelineFramework
**PipelineFramework** — это гибкий и лёгкий фреймворк для построения конвейеров обработки данных и логики в .NET-приложениях.
Он позволяет создавать последовательности шагов (middleware), которые обрабатывают входные данные, управляют потоком выполнения и обеспечивают расширяемость.
## 🚀 Возможности
- Простое определение шагов конвейера
- Поддержка асинхронной обработки
- Встроенная DI-интеграция
- Расширяемость через интерфейсы
- Минимум зависимостей
## 📦 Установка
```bash
dotnet add package PipelineFramework
```
## 🧩 Пример использования
```csharp
var pipeline = new PipelineBuilder<string>()
.Use(async (input, next) =>
{
Console.WriteLine($"Step 1: {input}");
await next(input + " → Step1");
})
.Use(async (input, next) =>
{
Console.WriteLine($"Step 2: {input}");
await next(input + " → Step2");
})
.Build();
await pipeline.ExecuteAsync("Start");
```
## 📚 Документация
- [Примеры использования](docs/examples.md)
- [Интеграция с DI](docs/di.md)
- [Расширение фреймворка](docs/extending.md)
## 🛠 Требования
- .NET 9.0 или выше
## 🧑‍💻 Автор
Разработано [FrigaT](https://github.com/FrigaT)
## 📄 Лицензия
Проект распространяется под лицензией [MIT](LICENSE)

View File

@@ -0,0 +1,6 @@
namespace PipelineFramework.Abstractions;
public interface IPipeline<TContext>
{
Task ExecuteAsync(TContext context);
}

View File

@@ -0,0 +1,7 @@
namespace PipelineFramework.Abstractions;
public interface IPipelineHook<TContext>
{
Task OnBeforeExecuteAsync(TContext context);
Task OnAfterExecuteAsync(TContext context);
}

View File

@@ -0,0 +1,6 @@
namespace PipelineFramework.Abstractions;
public interface IPipelineMiddleware<TContext>
{
Task InvokeAsync(TContext context, Func<Task> next);
}

View File

@@ -0,0 +1,8 @@
namespace PipelineFramework.Abstractions;
public interface IPipelineModule<TContext>
{
string Id { get; }
IEnumerable<string> DependsOn { get; }
Task ExecuteAsync(TContext context);
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.8" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\PipelineFramework\PipelineFramework.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,20 @@
using PipelineFramework;
namespace Microsoft.Extensions.DependencyInjection;
public static class PipelineServiceCollectionExtensions
{
public static IServiceCollection AddPipeline<TContext>(this IServiceCollection services)
{
services.AddTransient<IPipeline<TContext>>(provider =>
{
var modules = provider.GetServices<IPipelineModule<TContext>>().ToList();
var middlewares = provider.GetServices<IPipelineMiddleware<TContext>>().ToList();
var hooks = provider.GetServices<IPipelineHook<TContext>>().ToList();
return new Pipeline<TContext>(modules, middlewares, hooks);
});
return services;
}
}

View File

@@ -0,0 +1,6 @@
namespace PipelineFramework;
public interface IPipeline<TContext>
{
Task RunAsync(TContext context);
}

View File

@@ -0,0 +1,8 @@
namespace PipelineFramework;
public interface IPipelineHook<TContext>
{
Task OnBeforeAsync(TContext context, IPipelineModule<TContext> module);
Task OnAfterAsync(TContext context, IPipelineModule<TContext> module);
Task OnErrorAsync(TContext context, IPipelineModule<TContext> module, Exception ex);
}

View File

@@ -0,0 +1,6 @@
namespace PipelineFramework;
public interface IPipelineMiddleware<TContext>
{
Task InvokeAsync(TContext context, Func<Task> next);
}

View File

@@ -0,0 +1,9 @@
namespace PipelineFramework;
public interface IPipelineModule<TContext>
{
string Id { get; }
IEnumerable<string> DependsOn { get; }
Task ExecuteAsync(TContext context);
}

View File

@@ -0,0 +1,70 @@
namespace PipelineFramework;
public class Pipeline<TContext> : IPipeline<TContext>
{
private readonly List<IPipelineModule<TContext>> _modules;
private readonly List<IPipelineMiddleware<TContext>> _middlewares;
private readonly List<IPipelineHook<TContext>> _hooks;
public Pipeline(
List<IPipelineModule<TContext>> modules,
List<IPipelineMiddleware<TContext>> middlewares,
List<IPipelineHook<TContext>> hooks)
{
_modules = modules;
_middlewares = middlewares;
_hooks = hooks;
}
public async Task RunAsync(TContext context)
{
var executed = new HashSet<string>();
var moduleMap = _modules.ToDictionary(m => m.Id);
Func<Task> pipeline = async () =>
{
while (executed.Count < _modules.Count)
{
var ready = _modules
.Where(m => !executed.Contains(m.Id) &&
m.DependsOn.All(dep => executed.Contains(dep)))
.ToList();
var tasks = ready.Select(m => ExecuteModuleWithHooksAsync(m, context)).ToList();
await Task.WhenAll(tasks);
foreach (var m in ready)
executed.Add(m.Id);
}
};
foreach (var middleware in _middlewares.Reverse())
{
var next = pipeline;
pipeline = () => middleware(context, next);
}
await pipeline();
}
private async Task ExecuteModuleWithHooksAsync(IPipelineModule<TContext> module, TContext context)
{
try
{
foreach (var hook in _hooks)
await hook.OnBeforeAsync(context, module);
await module.ExecuteAsync(context);
foreach (var hook in _hooks)
await hook.OnAfterAsync(context, module);
}
catch (Exception ex)
{
foreach (var hook in _hooks)
await hook.OnErrorAsync(context, module, ex);
throw;
}
}
}

View File

@@ -0,0 +1,38 @@
namespace PipelineFramework;
public abstract class PipelineBuilder<TContext>
{
private readonly List<IPipelineModule<TContext>> _modules = new();
private readonly List<Func<TContext, Func<Task>, Task>> _middlewares = new();
private readonly List<IPipelineHook<TContext>> _hooks = new();
public PipelineBuilder<TContext> AddModule(IPipelineModule<TContext> module)
{
_modules.Add(module);
return this;
}
public PipelineBuilder<TContext> UseMiddleware<T>() where T : IPipelineMiddleware<TContext>, new()
{
_middlewares.Add((ctx, next) => new T().InvokeAsync(ctx, next));
return this;
}
public PipelineBuilder<TContext> UseMiddleware(Func<TContext, Func<Task>, Task> middleware)
{
_middlewares.Add(middleware);
return this;
}
public PipelineBuilder<TContext> AddHook(IPipelineHook<TContext> hook)
{
_hooks.Add(hook);
return this;
}
protected IReadOnlyList<IPipelineModule<TContext>> Modules => _modules;
protected IReadOnlyList<Func<TContext, Func<Task>, Task>> Middlewares => _middlewares;
protected IReadOnlyList<IPipelineHook<TContext>> Hooks => _hooks;
public abstract IPipeline<TContext> Build();
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>