diff --git a/Module/README.md b/Module/README.md index 0df2e34..f249585 100644 --- a/Module/README.md +++ b/Module/README.md @@ -1,18 +1,27 @@ # GenHTTP.Modules.Git -Serves **virtual git repositories** from a [GenHTTP](https://genhttp.org/) handler. Clones, fetches and pushes are -answered from your own data model via the smart HTTP protocol - without a repository on disk and without the `git` -binary. +A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push +with the regular `git` client, while every request is answered from your data model (a database, a blob store, an +API, ...) - without a repository on disk and without the `git` binary on the server. Use it to offer versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and agents that know how to use git. +The package is a module for [GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for +.NET. It provides a request handler that speaks git's smart HTTP protocol, which you can host in a GenHTTP +application or map to a path of an existing ASP.NET Core application. + ## Getting started -Add the package to a project hosting a GenHTTP server: +Create a console application and add the module along with a GenHTTP engine, which runs the server: ```sh +dotnet new console -n GitDemo +cd GitDemo + dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Engine.Internal +dotnet add package GenHTTP.Modules.Practices ``` Serve a repository via `GitServer.Create()`. The `InMemoryGitRepository` behaves like a bare repository and is a good @@ -43,6 +52,35 @@ await Host.Create() git clone http://localhost:8080/ hello ``` +## Using ASP.NET Core + +With the [GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package, the +handler can be mapped to a path of an existing ASP.NET Core application: + +```sh +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Adapters.AspNetCore +``` + +```csharp +using GenHTTP.Adapters.AspNetCore; + +using GenHTTP.Modules.Git; + +var builder = WebApplication.CreateBuilder(args); + +// Kestrel accepts 30 MB request bodies by default, allow larger pushes +builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024); + +var app = builder.Build(); + +// serve the repository (created as shown above) at /repo, +// all other requests are handled by your application +app.Map("/repo", GitServer.Create().Repository(repository)); + +await app.RunAsync(); +``` + ## Serving your own data Implement `IGitRepository` to describe your data as branches, tags, commits and files: @@ -101,7 +139,7 @@ public async ValueTask PushAsync(GitPush push) - Truncated histories (e.g. deleted old versions) are served as shallow repositories - Pushes with deltas, push options (`git push -o`) and messages - Regular files, executables and symbolic links, loaded lazily -- Single or multiple repositories per handler, all GenHTTP engines and concerns (e.g. authentication) +- Single or multiple repositories per handler, all GenHTTP engines and concerns (e.g. authentication), ASP.NET Core - Protection against crafted requests (size and file limits, validated file names) Annotated tags, submodules, partial clones (`--filter`), the dumb HTTP protocol and SHA-256 repositories are not diff --git a/README.md b/README.md index de85b2b..b4c6989 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,45 @@ # Virtual Git Server -A [GenHTTP](https://genhttp.org/) module that serves **virtual git repositories**: clones, fetches and pushes are -answered from your own data model over the smart HTTP protocol, without a repository on disk and without the `git` -binary. Use it to expose versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and -agents that know how to use git. +A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push +with the regular `git` client, while every request is answered from your data model (a database, a blob store, an +API, ...) - without a repository on disk and without the `git` binary on the server. + +Use it to expose versioned content (documents, configurations, apps, snippets) to developers, CI pipelines and coding +agents that know how to use git, instead of building custom sync tools. [![nuget Package](https://img.shields.io/nuget/v/GenHTTP.Modules.Git.svg)](https://www.nuget.org/packages/GenHTTP.Modules.Git/) [![Build](https://github.com/Kaliumhexacyanoferrat/virtual-git-server/actions/workflows/build.yml/badge.svg)](https://github.com/Kaliumhexacyanoferrat/virtual-git-server/actions/workflows/build.yml) [![View - Documentation](https://img.shields.io/badge/view-Documentation-AB54FF)](https://kaliumhexacyanoferrat.github.io/virtual-git-server/) +## How it fits in + +The library is distributed as the NuGet package `GenHTTP.Modules.Git`. It is a module for +[GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for .NET: the module provides a +request handler that speaks git's smart HTTP protocol, and you decide where in your web application it lives. + +You can use it in two ways: + +- **In a GenHTTP application** - the module plugs into a GenHTTP server like any other handler, either as the only + content or next to websites, APIs and files ([Getting Started](#getting-started)). +- **In an ASP.NET Core application** - an adapter maps the handler to a path of your existing `WebApplication`, next + to your controllers and minimal APIs ([Using ASP.NET Core](#using-aspnet-core)). + +In both cases, your code describes the repository (branches, commits and files) and decides what to do with pushes; +the module handles the git protocol. + ## Getting Started -Add the `GenHTTP.Modules.Git` package to your project and serve a repository via `GitServer.Create()`: +You need the [.NET SDK](https://dotnet.microsoft.com/download) (10 or newer). Create a console application and add +the module along with a GenHTTP engine to run the server: + +```sh +dotnet new console -n GitDemo +cd GitDemo + +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Engine.Internal +dotnet add package GenHTTP.Modules.Practices +``` + +Replace the content of `Program.cs` to serve a repository via `GitServer.Create()`: ```csharp using GenHTTP.Engine.Internal; @@ -17,26 +47,85 @@ using GenHTTP.Engine.Internal; using GenHTTP.Modules.Git; using GenHTTP.Modules.Practices; +// a repository that lives in memory, useful for trying things out var repository = new InMemoryGitRepository(); await repository.CommitAsync("main", GitTree.Create() .Add("README.md", "# Hello World") .Build(), "Initial commit"); +// the handler answering git requests var git = GitServer.Create() .Repository(repository); +// a GenHTTP server listening on port 8080 await Host.Create() .Handler(git) .Defaults() .RunAsync(); ``` +Start the application with `dotnet run` and use git as with any other server: + ```sh git clone http://localhost:8080/ hello ``` -To serve your own data, implement `IGitRepository` (read-only) or `IWritableGitRepository` (accepting pushes): +To combine the repository with other content, add it to a layout from the `GenHTTP.Modules.Layouting` package +(e.g. `Layout.Create().Add("repo", git)` to serve it at `/repo`). See the [GenHTTP documentation](https://genhttp.org/documentation/) for everything else a GenHTTP +server can do. + +## Using ASP.NET Core + +If you already have an ASP.NET Core application, the +[GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package lets you map the +git handler to a path, so you do not need to run a separate server: + +```sh +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Adapters.AspNetCore +``` + +```csharp +using GenHTTP.Adapters.AspNetCore; + +using GenHTTP.Modules.Git; + +var repository = new InMemoryGitRepository(); + +await repository.CommitAsync("main", GitTree.Create() + .Add("README.md", "# Hello World") + .Build(), "Initial commit"); + +var builder = WebApplication.CreateBuilder(args); + +// Kestrel accepts 30 MB request bodies by default, allow larger pushes +builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024); + +var app = builder.Build(); + +// serve the repository at /repo +app.Map("/repo", GitServer.Create().Repository(repository)); + +// the rest of your application +app.MapGet("/", () => "Hello from ASP.NET Core"); + +await app.RunAsync(); +``` + +```sh +# use the address your application listens on +git clone http://localhost:5000/repo hello +``` + +The handler only answers requests below the mapped path, all other requests are passed on to the rest of your +application. The [documentation](https://kaliumhexacyanoferrat.github.io/virtual-git-server/aspnet-core/) covers +multiple repositories and authentication with ASP.NET Core. + +## Serving your own data + +The in-memory repository is a good start. To serve your own data, implement `IGitRepository` (read-only) or +`IWritableGitRepository` (accepting pushes): ```csharp public sealed class DocumentRepository : IWritableGitRepository @@ -69,7 +158,7 @@ commit ids stable, how pushes are validated and shows a complete example of a ve | Pushing | Creating, updating and deleting branches and tags, deltas, push options, `remote:` messages | | History | Truncated histories (e.g. deleted old versions) are served as shallow repositories | | Files | Regular files, executables and symbolic links, loaded on demand | -| Hosting | Single or multiple repositories, any GenHTTP engine, concerns such as authentication | +| Hosting | Single or multiple repositories, any GenHTTP engine or ASP.NET Core, concerns such as authentication | Not supported: annotated tags, submodules, partial clones (`--filter`), the dumb HTTP protocol and SHA-256 repositories. See [protocol support](https://kaliumhexacyanoferrat.github.io/virtual-git-server/protocol/) for details. diff --git a/docs/aspnet-core.md b/docs/aspnet-core.md new file mode 100644 index 0000000..75f1aa6 --- /dev/null +++ b/docs/aspnet-core.md @@ -0,0 +1,93 @@ +# ASP.NET Core + +The module is a regular GenHTTP handler, so it does not require a GenHTTP server. With the +[GenHTTP.Adapters.AspNetCore](https://www.nuget.org/packages/GenHTTP.Adapters.AspNetCore/) package, it can be +mapped to a path of an existing ASP.NET Core application and runs next to your controllers, Razor pages and +minimal APIs. + +## Installation + +```sh +dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Adapters.AspNetCore +``` + +## Mapping a repository + +The adapter adds a `Map` overload to `WebApplication` that accepts GenHTTP handlers: + +```csharp +using GenHTTP.Adapters.AspNetCore; + +using GenHTTP.Modules.Git; + +var repository = new InMemoryGitRepository(); + +await repository.CommitAsync("main", GitTree.Create() + .Add("README.md", "# Hello World") + .Build(), "Initial commit"); + +var builder = WebApplication.CreateBuilder(args); + +builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 128 * 1024 * 1024); + +var app = builder.Build(); + +app.Map("/repo", GitServer.Create().Repository(repository)); + +app.MapGet("/", () => "Hello from ASP.NET Core"); + +await app.RunAsync(); +``` + +```sh +# use the address your application listens on +git clone http://localhost:5000/repo hello +``` + +Requests below the mapped path are handled by the git server, all other requests continue through the ASP.NET Core +pipeline. + +The builder returned by `GitServer.Create()` is configured exactly as described in [Hosting](hosting.md), so limits, +resolvers and concerns work the same way. + +## Request size + +Kestrel rejects request bodies larger than 30 MB by default, while the module accepts pushes up to 128 MB +(`MaximumPushSize`). Larger pushes then fail with HTTP 413 before they reach the module, so raise the Kestrel limit +to at least the configured push size, as shown above. If the application runs behind IIS or a reverse proxy, their +limits apply as well. + +## Multiple repositories + +Resolvers work the same way as with a GenHTTP server. The path segment below the mapped path selects the +repository: + +```csharp +app.Map("/git", GitServer.Create() + .Repositories(async (request, name) => await store.FindRepositoryAsync(name))); + +// git clone http://localhost:5000/git/my-project +// git clone http://localhost:5000/git/my-project.git +``` + +## Authentication + +GenHTTP concerns can be added to the builder, so the [authentication](hosting.md#authentication) described for +GenHTTP servers also works within ASP.NET Core: + +```sh +dotnet add package GenHTTP.Modules.Authentication +``` + +```csharp +using GenHTTP.Modules.Authentication; + +app.Map("/repo", GitServer.Create() + .Repository(repository) + .Add(BasicAuthentication.Create().Add("jane", "secret"))); +``` + +The handler is invoked by the adapter outside of the ASP.NET Core routing, so endpoint metadata such as +`RequireAuthorization()` does not apply to it. Use a GenHTTP concern or a middleware registered before the mapping +to protect the repository. diff --git a/docs/getting-started.md b/docs/getting-started.md index cb89d34..f05d217 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,14 +1,25 @@ # Getting Started +The module is a .NET library that runs within a [GenHTTP](https://genhttp.org/) web server. This page creates a +small console application hosting such a server. If you would like to add a repository to an existing ASP.NET Core +application instead, see [ASP.NET Core](aspnet-core.md). + ## Installation -Add the module to a project hosting a GenHTTP server: +You need the [.NET SDK](https://dotnet.microsoft.com/download) (10 or newer). Create a console application and add +the module along with a GenHTTP engine, which runs the server: ```sh +dotnet new console -n GitDemo +cd GitDemo + dotnet add package GenHTTP.Modules.Git +dotnet add package GenHTTP.Engine.Internal +dotnet add package GenHTTP.Modules.Practices ``` -The module targets .NET 10 and .NET 11 and works with every GenHTTP engine (internal, Kestrel and ioxide). +The module targets .NET 10 and .NET 11 and works with every GenHTTP engine (internal, Kestrel and ioxide). The +`Practices` package adds recommended defaults such as compression to the server. ## Serving a repository @@ -39,7 +50,7 @@ await Host.Create() .RunAsync(); ``` -Run the project and use git as you would with any other server: +Run the project with `dotnet run` and use git as you would with any other server: ```sh git clone http://localhost:8080/ hello diff --git a/docs/hosting.md b/docs/hosting.md index 8ca4f45..326cf25 100644 --- a/docs/hosting.md +++ b/docs/hosting.md @@ -92,7 +92,8 @@ searched for in the newest 10,000 commits of the repository. ## Engines and concerns -The handler works with all GenHTTP engines. Concerns such as compression or client caching added via `Defaults()` do +The handler works with all GenHTTP engines and can also be used within an ASP.NET Core application, see +[ASP.NET Core](aspnet-core.md). Concerns such as compression or client caching added via `Defaults()` do not interfere with git: responses are marked as not cacheable, and request bodies compressed by git are detected regardless of whether a decompression concern already handled them. diff --git a/docs/index.md b/docs/index.md index 0d9f828..2bf380e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,8 +1,8 @@ # Virtual Git Server -`GenHTTP.Modules.Git` serves **virtual git repositories** from a [GenHTTP](https://genhttp.org/) handler. Every -clone, fetch and push is answered from your own data model over the smart HTTP protocol, with no repository on disk -and no `git` binary. +A C# library that turns your .NET application into a **git server for your own data**. Users clone, fetch and push +with the regular `git` client, while every request is answered from your data model (a database, a blob store, an +API, ...) - with no repository on disk and no `git` binary on the server. ```csharp var git = GitServer.Create() @@ -17,6 +17,20 @@ await Host.Create() git clone http://localhost:8080/ documents ``` +## How it fits in + +The library is distributed as the NuGet package `GenHTTP.Modules.Git`. It is a module for +[GenHTTP](https://genhttp.org/), a lightweight, embeddable web server framework for .NET: the module provides a +request handler that speaks git's smart HTTP protocol, and you decide where in your web application it lives. + +- **In a GenHTTP application**, the handler is added to the server like any other content, either on its own or + next to websites, APIs and files. See [Getting Started](getting-started.md). +- **In an ASP.NET Core application**, an adapter maps the handler to a path of your existing `WebApplication`. See + [ASP.NET Core](aspnet-core.md). + +In both cases, your code describes the repository (branches, commits and files) and decides what to do with pushes, +while the module handles the git protocol. + ## Why? Git is the interface developers, CI pipelines and coding agents already know. If your application manages versioned @@ -41,13 +55,14 @@ source of truth (your data model) and derives everything git needs from it on th | Pushing | Creating, updating and deleting branches and tags, deltas, push options, `remote:` messages | | History | Truncated histories (e.g. deleted old versions) are served as shallow repositories | | Files | Regular files, executables and symbolic links, loaded on demand | -| Hosting | Single or multiple repositories, any GenHTTP engine, concerns such as authentication | +| Hosting | Single or multiple repositories, any GenHTTP engine or ASP.NET Core, concerns such as authentication | ## Where to go next
- **[Getting Started](getting-started.md)**: serve your first repository in a few lines of code. +- **[ASP.NET Core](aspnet-core.md)**: add a repository to an existing ASP.NET Core application. - **[How It Works](concepts.md)**: references, commits, trees and why commit ids must never change. - **[Serving Content](repositories.md)**: implement `IGitRepository` for your data model. - **[Accepting Pushes](pushing.md)**: implement `IWritableGitRepository` and validate what clients send. diff --git a/mkdocs.yml b/mkdocs.yml index 3a820d2..4e5d4f8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: Virtual Git Server -site_description: Serve virtual git repositories from your own data model with GenHTTP. +site_description: Serve virtual git repositories from your own data model in .NET applications, with GenHTTP or ASP.NET Core. site_url: https://kaliumhexacyanoferrat.github.io/virtual-git-server/ site_author: Andreas Nägeli @@ -67,6 +67,7 @@ nav: - Serving Content: repositories.md - Accepting Pushes: pushing.md - Hosting: hosting.md + - ASP.NET Core: aspnet-core.md - Testing: testing.md - Examples: - Versioned Content: examples/versioned-content.md