feat: MinVer git-tag versioning + /version endpoint + container tags #23

Merged
bjoern merged 4 commits from feature/minver-versioning into main 2026-06-29 20:19:04 +02:00
Member

MinVer git-tag-based semantic versioning

How it works

  • Release tag (v0.1.0): MinVer produces 0.1.0 (clean)
  • On main, after tag: 0.1.0-dev.N+sha where N = commits since tag, sha = commit hash
  • Version flows into AssemblyInformationalVersion → startup log, /version endpoint, Docker container tag

Changes

File Change
Directory.Packages.props Added MinVer 6.0.0
Directory.Build.props (new) MinVerDefaultPreReleasePhase=dev
DoujinManager.Server.csproj MinVer PackageReference (PrivateAssets=all)
Program.cs Startup version log + GET /version endpoint
Dockerfile Install git in build stage
.dockerignore Stop excluding .git/ (MinVer needs it)
docker-publish.yml Use minver-cli for container tag; trigger on v* tags too

Version endpoints

  • GET /version{"version":"0.1.0-dev.5+abc1234"} (anonymous)
  • Startup log: DoujinManager Server v0.1.0-dev.5+abc1234
  • Container image: git.kagaku.eu/teamai/doujin-manager:0.1.0-dev.5+abc1234

What you need to do after merge

Create the baseline tag so MinVer has a starting point:

git tag v0.1.0
git push --tags

Future releases: git tag v0.2.0 && git push --tags — the docker-publish pipeline will automatically build and tag the container.

See ADR 0020 for the full decision record.

## MinVer git-tag-based semantic versioning ### How it works - **Release tag** (`v0.1.0`): MinVer produces `0.1.0` (clean) - **On main, after tag**: `0.1.0-dev.N+sha` where N = commits since tag, sha = commit hash - Version flows into `AssemblyInformationalVersion` → startup log, `/version` endpoint, Docker container tag ### Changes | File | Change | |---|---| | `Directory.Packages.props` | Added MinVer 6.0.0 | | `Directory.Build.props` (new) | `MinVerDefaultPreReleasePhase=dev` | | `DoujinManager.Server.csproj` | MinVer PackageReference (PrivateAssets=all) | | `Program.cs` | Startup version log + `GET /version` endpoint | | `Dockerfile` | Install git in build stage | | `.dockerignore` | Stop excluding `.git/` (MinVer needs it) | | `docker-publish.yml` | Use minver-cli for container tag; trigger on `v*` tags too | ### Version endpoints - `GET /version` → `{"version":"0.1.0-dev.5+abc1234"}` (anonymous) - Startup log: `DoujinManager Server v0.1.0-dev.5+abc1234` - Container image: `git.kagaku.eu/teamai/doujin-manager:0.1.0-dev.5+abc1234` ### What you need to do after merge Create the baseline tag so MinVer has a starting point: ```bash git tag v0.1.0 git push --tags ``` Future releases: `git tag v0.2.0 && git push --tags` — the docker-publish pipeline will automatically build and tag the container. See ADR 0020 for the full decision record.
feat: MinVer git-tag versioning + /version endpoint + container tags
Some checks failed
CI / build (pull_request) Failing after 13s
CI / test (pull_request) Has been skipped
2ff1b8e0f7
Add MinVer to derive semantic versions from git tags:
- On release tag (v0.1.0): clean 0.1.0
- On main between tags: 0.1.0-dev.N+sha

Changes:
- MinVer 6.0.0 added to Server csproj (PrivateAssets=all)
- Directory.Build.props configures MinVerDefaultPreReleasePhase=dev
- Dockerfile installs git in build stage (MinVer needs it)
- .dockerignore no longer excludes .git/
- docker-publish.yml runs minver-cli to get version for container tag,
  triggers on both main pushes and v* tags
- Program.cs: startup log prints version, GET /version endpoint added
- ADR 0020 documents the decision
fix: add System.Reflection using + fix MinVer deprecation warning
All checks were successful
CI / build (pull_request) Successful in 18s
CI / test (pull_request) Successful in 38s
8c22d716b6
- Add 'using System.Reflection' (GetCustomAttribute is an extension method)
- MinVerDefaultPreReleasePhase → MinVerDefaultPreReleaseIdentifiers (dev.0)
- Clean up fully-qualified System.Reflection references
- Update minver-cli flags in docker-publish.yml to match

Summary

Summary
Generated on: 06/29/2026 - 18:12:31
Coverage date: 06/29/2026 - 18:12:15 - 06/29/2026 - 18:12:29
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 208
Files: 95
Line coverage: 83.6% (4302 of 5144)
Covered lines: 4302
Uncovered lines: 842
Coverable lines: 5144
Total lines: 8916
Branch coverage: 49.5% (370 of 746)
Covered branches: 370
Total branches: 746
Method coverage: Feature is only available for sponsors

Coverage

DoujinManager.ApplicationCore - 84.8%
Name Line Branch
DoujinManager.ApplicationCore 84.8% ****
DoujinManager.ApplicationCore.Entities.Chapter 87.5%
DoujinManager.ApplicationCore.Entities.Circle 100%
DoujinManager.ApplicationCore.Entities.Doujin 100%
DoujinManager.ApplicationCore.Entities.DoujinCircle 75%
DoujinManager.ApplicationCore.Entities.DoujinPerson 80%
DoujinManager.ApplicationCore.Entities.DoujinTag 75%
DoujinManager.ApplicationCore.Entities.ImageFile 100%
DoujinManager.ApplicationCore.Entities.Page 80%
DoujinManager.ApplicationCore.Entities.Person 100%
DoujinManager.ApplicationCore.Entities.Tag 100%
DoujinManager.ApplicationCore.Entities.Title 83.3%
DoujinManager.ApplicationCore.Entities.Variant 91.6%
DoujinManager.ApplicationCore.Ids.ChapterId 66.6%
DoujinManager.ApplicationCore.Ids.CircleId 66.6%
DoujinManager.ApplicationCore.Ids.DoujinId 100%
DoujinManager.ApplicationCore.Ids.ImageFileId 66.6%
DoujinManager.ApplicationCore.Ids.PageId 66.6%
DoujinManager.ApplicationCore.Ids.PersonId 66.6%
DoujinManager.ApplicationCore.Ids.TagId 66.6%
DoujinManager.ApplicationCore.Ids.TitleId 66.6%
DoujinManager.ApplicationCore.Ids.VariantId 66.6%
DoujinManager.ApplicationCore.Ports.ExtractedImage 100%
DoujinManager.ApplicationCore.Ports.ImageInspection 100%
DoujinManager.ApplicationCore.Services.BackupInfo 100%
DoujinManager.ApplicationCore.Services.ITagService 100%
DoujinManager.ApplicationCore.Services.ServiceResult 100%
DoujinManager.ApplicationCore.Services.ServiceResult`1 33.3%
DoujinManager.ApplicationCore.Services.VoidResult 88.8%
DoujinManager.ApplicationCore.UseCases.AddTitleCommand 0%
DoujinManager.ApplicationCore.UseCases.AssignCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignPersonCommand 100%
DoujinManager.ApplicationCore.UseCases.AssignTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateChapterCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateCircleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.CreatePersonCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTagCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateTitleCommand 100%
DoujinManager.ApplicationCore.UseCases.CreateVariantCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand 0%
DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.DeletePageCommand 100%
DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand 0%
DoujinManager.ApplicationCore.UseCases.GetDoujinQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageQuery 100%
DoujinManager.ApplicationCore.UseCases.GetImageResult 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery 100%
DoujinManager.ApplicationCore.UseCases.GetThumbnailResult 100%
DoujinManager.ApplicationCore.UseCases.GetVariantQuery 100%
DoujinManager.ApplicationCore.UseCases.ListChaptersQuery 100%
DoujinManager.ApplicationCore.UseCases.ListCirclesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPagesQuery 100%
DoujinManager.ApplicationCore.UseCases.ListPeopleQuery 100%
DoujinManager.ApplicationCore.UseCases.ListTagsQuery 100%
DoujinManager.ApplicationCore.UseCases.ListVariantsQuery 100%
DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand 0%
DoujinManager.ApplicationCore.UseCases.RemovePersonCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveTagCommand 0%
DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand 0%
DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery 100%
DoujinManager.ApplicationCore.UseCases.SearchResult 100%
DoujinManager.ApplicationCore.UseCases.SearchResults 100%
DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand 100%
DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand 0%
DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand 0%
DoujinManager.ApplicationCore.UseCases.UploadImageFile 100%
DoujinManager.ApplicationCore.UseCases.UploadPagesCommand 100%
DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand 100%
DoujinManager.Infrastructure - 91.6%
Name Line Branch
DoujinManager.Infrastructure 91.6% 70.5%
DoujinManager.Infrastructure.Archives.ZipExtractor 100% 87.5%
DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PageConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TagConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration 100%
DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration 100%
DoujinManager.Infrastructure.Data.DoujinManagerDbContext 100%
DoujinManager.Infrastructure.Data.GuidIdGenerator 11.1%
DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna
pshot
100%
DoujinManager.Infrastructure.Data.Migrations.InitialCreate 97.1%
DoujinManager.Infrastructure.Data.ModelBuilderExtensions 50%
DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory 69.2%
DoujinManager.Infrastructure.Images.SkiaSharpImageInspector 88.2% 70.9%
DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator 94.5% 66.6%
DoujinManager.Infrastructure.Services.BackupService 85.1% 66.6%
DoujinManager.Infrastructure.Services.ChapterService 54.2% 25%
DoujinManager.Infrastructure.Services.CircleService 69.6% 0%
DoujinManager.Infrastructure.Services.DoujinService 70.3% 45.8%
DoujinManager.Infrastructure.Services.ImageService 92% 50%
DoujinManager.Infrastructure.Services.PageService 84.9% 65%
DoujinManager.Infrastructure.Services.PersonService 69.6% 0%
DoujinManager.Infrastructure.Services.SearchService 100% 98.2%
DoujinManager.Infrastructure.Services.TagService 94.7% 100%
DoujinManager.Infrastructure.Services.VariantService 50.7% 16.6%
DoujinManager.Infrastructure.Storage.FilesystemImageStorage 100% 100%
DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage 95% 50%
DoujinManager.Infrastructure.UseCases.AddTitleUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.AssignCircleUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignPersonUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.AssignTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateChapterUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateCircleUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase 100% 98%
DoujinManager.Infrastructure.UseCases.CreatePersonUseCase 100%
DoujinManager.Infrastructure.UseCases.CreateTagUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.CreateVariantUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase 0%
DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.DeletePageUseCase 92.3% 75%
DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase 0%
DoujinManager.Infrastructure.UseCases.GetDoujinUseCase 100%
DoujinManager.Infrastructure.UseCases.GetImageUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.GetVariantUseCase 100%
DoujinManager.Infrastructure.UseCases.ListChaptersUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListCirclesUseCase 100%
DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.ListPeopleUseCase 100%
DoujinManager.Infrastructure.UseCases.ListTagsUseCase 100%
DoujinManager.Infrastructure.UseCases.ListVariantsUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase 0%
DoujinManager.Infrastructure.UseCases.RemovePersonUseCase 0%
DoujinManager.Infrastructure.UseCases.RemoveTagUseCase 0%
DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase 0%
DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase 100%
DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase 100% 100%
DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase 0% 0%
DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase 0%
DoujinManager.Infrastructure.UseCases.UploadPagesUseCase 91.1% 92.8%
DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase 70.2% 62.5%
DoujinManager.RestAdapter - 84.2%
Name Line Branch
DoujinManager.RestAdapter 84.2% 74.1%
DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware 100% 91.6%
DoujinManager.RestAdapter.Dtos.AssignTagDto 0%
DoujinManager.RestAdapter.Dtos.BackupDto 100%
DoujinManager.RestAdapter.Dtos.ChapterDto 100%
DoujinManager.RestAdapter.Dtos.CircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateChapterDto 100%
DoujinManager.RestAdapter.Dtos.CreateCircleDto 100%
DoujinManager.RestAdapter.Dtos.CreateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.CreatePersonDto 100%
DoujinManager.RestAdapter.Dtos.CreateTagDto 100%
DoujinManager.RestAdapter.Dtos.CreateTitleDto 100%
DoujinManager.RestAdapter.Dtos.CreateVariantDto 100%
DoujinManager.RestAdapter.Dtos.DoujinDetailDto 100%
DoujinManager.RestAdapter.Dtos.DoujinPersonDto 100%
DoujinManager.RestAdapter.Dtos.DoujinSummaryDto 100%
DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto 0%
DoujinManager.RestAdapter.Dtos.LinkCircleDto 0%
DoujinManager.RestAdapter.Dtos.LinkPersonDto 100%
DoujinManager.RestAdapter.Dtos.PageDetailDto 100%
DoujinManager.RestAdapter.Dtos.PageDto 100%
DoujinManager.RestAdapter.Dtos.PersonDto 100%
DoujinManager.RestAdapter.Dtos.ReorderPagesDto 100%
DoujinManager.RestAdapter.Dtos.SearchDoujinsDto 100%
DoujinManager.RestAdapter.Dtos.SearchResultDto 100%
DoujinManager.RestAdapter.Dtos.TagDto 100%
DoujinManager.RestAdapter.Dtos.TitleDto 100%
DoujinManager.RestAdapter.Dtos.UpdateChapterDto 0%
DoujinManager.RestAdapter.Dtos.UpdateCircleDto 0%
DoujinManager.RestAdapter.Dtos.UpdateDoujinDto 100%
DoujinManager.RestAdapter.Dtos.UpdatePersonDto 0%
DoujinManager.RestAdapter.Dtos.UpdateVariantDto 0%
DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto 100%
DoujinManager.RestAdapter.Dtos.VariantDetailDto 100%
DoujinManager.RestAdapter.Dtos.VariantSummaryDto 100%
DoujinManager.RestAdapter.Endpoints.BackupEndpoints 100%
DoujinManager.RestAdapter.Endpoints.DoujinEndpoints 81.2% 75%
DoujinManager.RestAdapter.Endpoints.ImageEndpoints 96.6% 50%
DoujinManager.RestAdapter.Endpoints.MetadataEndpoints 84.2%
DoujinManager.RestAdapter.Endpoints.PaginationParams 100%
DoujinManager.RestAdapter.Endpoints.SearchEndpoints 100% 50%
DoujinManager.RestAdapter.Endpoints.VariantEndpoints 69.9% 37.5%
DoujinManager.RestAdapter.Envelopes.CollectionResponse`1 83.3%
DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults 0%
DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions 100%
DoujinManager.RestAdapter.Envelopes.ErrorResponse 100%
DoujinManager.RestAdapter.Envelopes.HypermediaAction 100%
DoujinManager.RestAdapter.Envelopes.HypermediaHelpers 87% 100%
DoujinManager.RestAdapter.Envelopes.Link 100%
DoujinManager.RestAdapter.Envelopes.PageInfo 100%
DoujinManager.RestAdapter.Envelopes.ResourceResponse`1 80%
DoujinManager.RestAdapter.Envelopes.ValidationError 100%
DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse 100%
DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware 100% 50%
DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware 100% 100%
DoujinManager.RestAdapter.RestAdapterExtensions 100% 100%
Microsoft.Extensions.Validation.Generated 78.6% 78.2%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 62.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 21.8%
Name Line Branch
DoujinManager.Server 21.8% 0.8%
DoujinManager.Server.ImageInfrastructureRegistration 100%
DoujinManager.Server.ProxyAwareServerTransformer 100% 50%
DoujinManager.Server.ScalarUi 100%
DoujinManager.Server.UseCaseRegistrationHelper 100%
Microsoft.AspNetCore.OpenApi.Generated 0% 0%
Program 0% 0%
System.Runtime.CompilerServices 0%
<!-- coverage-comment --> # Summary <details open><summary>Summary</summary> ||| |:---|:---| | Generated on: | 06/29/2026 - 18:12:31 | | Coverage date: | 06/29/2026 - 18:12:15 - 06/29/2026 - 18:12:29 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 208 | | Files: | 95 | | **Line coverage:** | 83.6% (4302 of 5144) | | Covered lines: | 4302 | | Uncovered lines: | 842 | | Coverable lines: | 5144 | | Total lines: | 8916 | | **Branch coverage:** | 49.5% (370 of 746) | | Covered branches: | 370 | | Total branches: | 746 | | **Method coverage:** | [Feature is only available for sponsors](https://reportgenerator.io/pro) | </details> ## Coverage <details><summary>DoujinManager.ApplicationCore - 84.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.ApplicationCore**|**84.8%**|****| |DoujinManager.ApplicationCore.Entities.Chapter|87.5%|| |DoujinManager.ApplicationCore.Entities.Circle|100%|| |DoujinManager.ApplicationCore.Entities.Doujin|100%|| |DoujinManager.ApplicationCore.Entities.DoujinCircle|75%|| |DoujinManager.ApplicationCore.Entities.DoujinPerson|80%|| |DoujinManager.ApplicationCore.Entities.DoujinTag|75%|| |DoujinManager.ApplicationCore.Entities.ImageFile|100%|| |DoujinManager.ApplicationCore.Entities.Page|80%|| |DoujinManager.ApplicationCore.Entities.Person|100%|| |DoujinManager.ApplicationCore.Entities.Tag|100%|| |DoujinManager.ApplicationCore.Entities.Title|83.3%|| |DoujinManager.ApplicationCore.Entities.Variant|91.6%|| |DoujinManager.ApplicationCore.Ids.ChapterId|66.6%|| |DoujinManager.ApplicationCore.Ids.CircleId|66.6%|| |DoujinManager.ApplicationCore.Ids.DoujinId|100%|| |DoujinManager.ApplicationCore.Ids.ImageFileId|66.6%|| |DoujinManager.ApplicationCore.Ids.PageId|66.6%|| |DoujinManager.ApplicationCore.Ids.PersonId|66.6%|| |DoujinManager.ApplicationCore.Ids.TagId|66.6%|| |DoujinManager.ApplicationCore.Ids.TitleId|66.6%|| |DoujinManager.ApplicationCore.Ids.VariantId|66.6%|| |DoujinManager.ApplicationCore.Ports.ExtractedImage|100%|| |DoujinManager.ApplicationCore.Ports.ImageInspection|100%|| |DoujinManager.ApplicationCore.Services.BackupInfo|100%|| |DoujinManager.ApplicationCore.Services.ITagService|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult|100%|| |DoujinManager.ApplicationCore.Services.ServiceResult`1|33.3%|| |DoujinManager.ApplicationCore.Services.VoidResult|88.8%|| |DoujinManager.ApplicationCore.UseCases.AddTitleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.AssignCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignPersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.AssignTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateChapterCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateCircleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreatePersonCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTagCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateTitleCommand|100%|| |DoujinManager.ApplicationCore.UseCases.CreateVariantCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteChapterCommand|0%|| |DoujinManager.ApplicationCore.UseCases.DeleteDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeletePageCommand|100%|| |DoujinManager.ApplicationCore.UseCases.DeleteVariantCommand|0%|| |DoujinManager.ApplicationCore.UseCases.GetDoujinQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetImageResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailQuery|100%|| |DoujinManager.ApplicationCore.UseCases.GetThumbnailResult|100%|| |DoujinManager.ApplicationCore.UseCases.GetVariantQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListChaptersQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListCirclesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPagesQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListPeopleQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListTagsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.ListVariantsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.RemoveCircleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemovePersonCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveTagCommand|0%|| |DoujinManager.ApplicationCore.UseCases.RemoveTitleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.ReorderPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.SearchDoujinsQuery|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResult|100%|| |DoujinManager.ApplicationCore.UseCases.SearchResults|100%|| |DoujinManager.ApplicationCore.UseCases.UpdateChapterCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateCircleCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateDoujinCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UpdatePersonCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UpdateVariantCommand|0%|| |DoujinManager.ApplicationCore.UseCases.UploadImageFile|100%|| |DoujinManager.ApplicationCore.UseCases.UploadPagesCommand|100%|| |DoujinManager.ApplicationCore.UseCases.UploadZipPagesCommand|100%|| </details> <details><summary>DoujinManager.Infrastructure - 91.6%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**91.6%**|**70.5%**| |DoujinManager.Infrastructure.Archives.ZipExtractor|100%|87.5%| |DoujinManager.Infrastructure.Data.Configurations.ChapterConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.CircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinCircleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinPersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.DoujinTagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.ImageFileConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PageConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.PersonConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TagConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.TitleConfiguration|100%|| |DoujinManager.Infrastructure.Data.Configurations.VariantConfiguration|100%|| |DoujinManager.Infrastructure.Data.DoujinManagerDbContext|100%|| |DoujinManager.Infrastructure.Data.GuidIdGenerator|11.1%|| |DoujinManager.Infrastructure.Data.Migrations.DoujinManagerDbContextModelSna<br/>pshot|100%|| |DoujinManager.Infrastructure.Data.Migrations.InitialCreate|97.1%|| |DoujinManager.Infrastructure.Data.ModelBuilderExtensions|50%|| |DoujinManager.Infrastructure.Data.StronglyTypedIdConverterFactory|69.2%|| |DoujinManager.Infrastructure.Images.SkiaSharpImageInspector|88.2%|70.9%| |DoujinManager.Infrastructure.Images.SkiaSharpThumbnailGenerator|94.5%|66.6%| |DoujinManager.Infrastructure.Services.BackupService|85.1%|66.6%| |DoujinManager.Infrastructure.Services.ChapterService|54.2%|25%| |DoujinManager.Infrastructure.Services.CircleService|69.6%|0%| |DoujinManager.Infrastructure.Services.DoujinService|70.3%|45.8%| |DoujinManager.Infrastructure.Services.ImageService|92%|50%| |DoujinManager.Infrastructure.Services.PageService|84.9%|65%| |DoujinManager.Infrastructure.Services.PersonService|69.6%|0%| |DoujinManager.Infrastructure.Services.SearchService|100%|98.2%| |DoujinManager.Infrastructure.Services.TagService|94.7%|100%| |DoujinManager.Infrastructure.Services.VariantService|50.7%|16.6%| |DoujinManager.Infrastructure.Storage.FilesystemImageStorage|100%|100%| |DoujinManager.Infrastructure.Storage.FilesystemThumbnailStorage|95%|50%| |DoujinManager.Infrastructure.UseCases.AddTitleUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.AssignCircleUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignPersonUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.AssignTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateChapterUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateCircleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateDoujinUseCase|100%|98%| |DoujinManager.Infrastructure.UseCases.CreatePersonUseCase|100%|| |DoujinManager.Infrastructure.UseCases.CreateTagUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.CreateVariantUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.DeleteChapterUseCase|0%|| |DoujinManager.Infrastructure.UseCases.DeleteDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.DeletePageUseCase|92.3%|75%| |DoujinManager.Infrastructure.UseCases.DeleteVariantUseCase|0%|| |DoujinManager.Infrastructure.UseCases.GetDoujinUseCase|100%|| |DoujinManager.Infrastructure.UseCases.GetImageUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetThumbnailUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.GetVariantUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListChaptersUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListCirclesUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.ListPeopleUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListTagsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.ListVariantsUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.RemoveCircleUseCase|0%|| |DoujinManager.Infrastructure.UseCases.RemovePersonUseCase|0%|| |DoujinManager.Infrastructure.UseCases.RemoveTagUseCase|0%|| |DoujinManager.Infrastructure.UseCases.RemoveTitleUseCase|0%|| |DoujinManager.Infrastructure.UseCases.ReorderPagesUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.SearchDoujinsUseCase|100%|| |DoujinManager.Infrastructure.UseCases.UpdateChapterUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateCircleUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateDoujinUseCase|100%|100%| |DoujinManager.Infrastructure.UseCases.UpdatePersonUseCase|0%|0%| |DoujinManager.Infrastructure.UseCases.UpdateVariantUseCase|0%|| |DoujinManager.Infrastructure.UseCases.UploadPagesUseCase|91.1%|92.8%| |DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase|70.2%|62.5%| </details> <details><summary>DoujinManager.RestAdapter - 84.2%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**84.2%**|**74.1%**| |DoujinManager.RestAdapter.Auth.StaticBearerTokenAuthMiddleware|100%|91.6%| |DoujinManager.RestAdapter.Dtos.AssignTagDto|0%|| |DoujinManager.RestAdapter.Dtos.BackupDto|100%|| |DoujinManager.RestAdapter.Dtos.ChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateChapterDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateCircleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.CreatePersonDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTagDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateTitleDto|100%|| |DoujinManager.RestAdapter.Dtos.CreateVariantDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.DoujinSummaryDto|100%|| |DoujinManager.RestAdapter.Dtos.ImageFileSummaryDto|0%|| |DoujinManager.RestAdapter.Dtos.LinkCircleDto|0%|| |DoujinManager.RestAdapter.Dtos.LinkPersonDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.PageDto|100%|| |DoujinManager.RestAdapter.Dtos.PersonDto|100%|| |DoujinManager.RestAdapter.Dtos.ReorderPagesDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchDoujinsDto|100%|| |DoujinManager.RestAdapter.Dtos.SearchResultDto|100%|| |DoujinManager.RestAdapter.Dtos.TagDto|100%|| |DoujinManager.RestAdapter.Dtos.TitleDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdateChapterDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateCircleDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateDoujinDto|100%|| |DoujinManager.RestAdapter.Dtos.UpdatePersonDto|0%|| |DoujinManager.RestAdapter.Dtos.UpdateVariantDto|0%|| |DoujinManager.RestAdapter.Dtos.UploadPagesResponseDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantDetailDto|100%|| |DoujinManager.RestAdapter.Dtos.VariantSummaryDto|100%|| |DoujinManager.RestAdapter.Endpoints.BackupEndpoints|100%|| |DoujinManager.RestAdapter.Endpoints.DoujinEndpoints|81.2%|75%| |DoujinManager.RestAdapter.Endpoints.ImageEndpoints|96.6%|50%| |DoujinManager.RestAdapter.Endpoints.MetadataEndpoints|84.2%|| |DoujinManager.RestAdapter.Endpoints.PaginationParams|100%|| |DoujinManager.RestAdapter.Endpoints.SearchEndpoints|100%|50%| |DoujinManager.RestAdapter.Endpoints.VariantEndpoints|69.9%|37.5%| |DoujinManager.RestAdapter.Envelopes.CollectionResponse`1|83.3%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeDefaults|0%|| |DoujinManager.RestAdapter.Envelopes.EnvelopeJsonOptions|100%|| |DoujinManager.RestAdapter.Envelopes.ErrorResponse|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaAction|100%|| |DoujinManager.RestAdapter.Envelopes.HypermediaHelpers|87%|100%| |DoujinManager.RestAdapter.Envelopes.Link|100%|| |DoujinManager.RestAdapter.Envelopes.PageInfo|100%|| |DoujinManager.RestAdapter.Envelopes.ResourceResponse`1|80%|| |DoujinManager.RestAdapter.Envelopes.ValidationError|100%|| |DoujinManager.RestAdapter.Envelopes.ValidationErrorResponse|100%|| |DoujinManager.RestAdapter.Middleware.GlobalExceptionMiddleware|100%|50%| |DoujinManager.RestAdapter.Middleware.RequestLoggingMiddleware|100%|100%| |DoujinManager.RestAdapter.RestAdapterExtensions|100%|100%| |Microsoft.Extensions.Validation.Generated|78.6%|78.2%| |Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C<br/>E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr<br/>ibuteCache|100%|62.5%| |System.Runtime.CompilerServices|0%|| </details> <details><summary>DoujinManager.Server - 21.8%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**21.8%**|**0.8%**| |DoujinManager.Server.ImageInfrastructureRegistration|100%|| |DoujinManager.Server.ProxyAwareServerTransformer|100%|50%| |DoujinManager.Server.ScalarUi|100%|| |DoujinManager.Server.UseCaseRegistrationHelper|100%|| |Microsoft.AspNetCore.OpenApi.Generated|0%|0%| |Program|0%|0%| |System.Runtime.CompilerServices|0%|| </details>
Author
Member

🤖 Hermes automated review: changes requested

Reviewed diff 5014af3...2ff1b8e (+106/-4, 9 files): MinVer git-tag versioning, GET /version endpoint, startup version log, Dockerfile git install, .dockerignore .git/ un-exclusion, and docker-publish.yml container-tag workflow changes. ADR 0020 is well written and the overall design is sound. Static security scan clean (no secrets, injection, eval/exec, pickle in added lines). However, the build fails and there is a Docker tag validity problem — both blocking.

Major (blocking)

1. Build break: Assembly.GetCustomAttribute<T>() not in scope — missing using System.Reflection;
backend/src/DoujinManager.Server/Program.cs:94 and :133

The PR adds code that calls Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>() at two sites (startup log line 94, /version endpoint line 133). GetCustomAttribute<T> is an extension method defined in CustomAttributeExtensions within the System.Reflection namespace, but Program.cs has no using System.Reflection; directive (the code uses the fully-qualified System.Reflection.AssemblyInformationalVersionAttribute type but never imports the namespace). This produces:

Program.cs(94,6): error CS1061: 'Assembly' does not contain a definition for
'GetCustomAttribute' and no accessible extension method 'GetCustomAttribute'
accepting a first argument of type 'Assembly' could be found
(are you missing a using directive or an assembly reference?)

(same error at line 133)

Build result: FAILED (2 errors). I verified this by checking out the PR head 2ff1b8e and running dotnet build locally. Adding using System.Reflection; at the top of Program.cs resolves both errors and the build succeeds. This also explains why the forgejo-actions coverage bot has posted no comment on this PR — ci.yml posts coverage only on a successful build+test, and the build is failing.

Suggested fix: add using System.Reflection; to the top of Program.cs, or change the calls to use the static Attribute.GetCustomAttribute(assembly, typeof(...)) form.

2. MinVer version string contains +, which is invalid in a Docker image tag
.github/workflows/docker-publish.yml:55git.kagaku.eu/teamai/doujin-manager:${{ steps.version.outputs.version }}

MinVer's pre-release output format is 0.1.0-dev.N+sha (e.g. 0.1.0-dev.5+abc1234). The + separator introduces SemVer build metadata and is not allowed in a Docker reference tag — valid tag characters are [a-z0-9_.-] only (must also be lowercase, see finding below re: the prior PR #12). docker buildx / the registry will reject a tag containing +. This means the build-and-push job will fail the moment MinVer emits a build-metadata segment (which is the default behavior — every non-release commit gets +sha).

Suggested fix: strip/sanitize the version before using it as a Docker tag. Either set MinVerBuildMetadata to empty (suppresses the +sha), or in the workflow step replace + with - (and - already being valid):

VERSION=$(minver ... | sed 's/+/-/')

The PR's own example (0.1.0-dev.5+abc1234) demonstrates the problematic character is always present for dev builds.

Minor (non-blocking)

3. MinVerDefaultPreReleasePhase is deprecated in MinVer 6
backend/Directory.Build.props:7 — MinVer warns:

MINVER1008: MinVerDefaultPreReleasePhase is deprecated and will be removed in the
next major version. Use MinVerDefaultPreReleaseIdentifiers instead, with an
additional "0" identifier (e.g. "preview.0" instead of "preview").

Since this is a brand-new adoption, worth using the non-deprecated MinVerDefaultPreReleaseIdentifiers = dev.0 property now to avoid a future break.

4. /version endpoint duplicates the reflection logic from the startup log
backend/src/DoujinManager.Server/Program.cs:92-97 (startup) and :130-141 (/version) both re-fetch the AssemblyInformationalVersionAttribute inline. Consider extracting a small static string GetVersion() helper (or reading it once into a variable/registered singleton) to avoid the duplicated reflection and keep a single source of truth.

5. .dockerignore .git/ un-exclusion increases build context size
.dockerignore:8 — Removing .git/ from the ignore list is correct and necessary for MinVer, but the .git directory can be large for long-lived repos. Worth a note that the build context will now carry full git history on every docker build. Not actionable now, just flagging for awareness.

Verification

  • CI/CD: No forgejo-actions coverage comment exists on this PR (0 comments), which is consistent with a failing build (ci.yml posts coverage only on success). Per CI-absent/failed policy, I ran local verification:
    • dotnet build src/DoujinManager.Server/DoujinManager.Server.csproj -c ReleaseFAILED (2 CS1061 errors, both GetCustomAttribute — see finding #1). Confirmed resolved by adding using System.Reflection;.
  • Diff computed locally via git diff between base 5014af3 and head 2ff1b8e.
  • Static security scan: clean on added lines (no secrets, shell injection, eval/exec, pickle, or SQL formatting).

Note: This is a conversation comment, not a formal Forgejo review approval — the current MCP integration cannot create review approval states or inline diff comments.

Automated daily review. I never merge PRs.

## 🤖 Hermes automated review: changes requested Reviewed diff `5014af3...2ff1b8e` (+106/-4, 9 files): MinVer git-tag versioning, `GET /version` endpoint, startup version log, Dockerfile git install, `.dockerignore` `.git/` un-exclusion, and `docker-publish.yml` container-tag workflow changes. ADR 0020 is well written and the overall design is sound. Static security scan clean (no secrets, injection, eval/exec, pickle in added lines). However, **the build fails** and there is a Docker tag validity problem — both blocking. ### Major (blocking) **1. Build break: `Assembly.GetCustomAttribute<T>()` not in scope — missing `using System.Reflection;`** `backend/src/DoujinManager.Server/Program.cs:94` and `:133` The PR adds code that calls `Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()` at two sites (startup log line 94, `/version` endpoint line 133). `GetCustomAttribute<T>` is an *extension method* defined in `CustomAttributeExtensions` within the `System.Reflection` namespace, but `Program.cs` has **no `using System.Reflection;` directive** (the code uses the fully-qualified `System.Reflection.AssemblyInformationalVersionAttribute` type but never imports the namespace). This produces: ``` Program.cs(94,6): error CS1061: 'Assembly' does not contain a definition for 'GetCustomAttribute' and no accessible extension method 'GetCustomAttribute' accepting a first argument of type 'Assembly' could be found (are you missing a using directive or an assembly reference?) ``` (same error at line 133) **Build result:** FAILED (2 errors). I verified this by checking out the PR head `2ff1b8e` and running `dotnet build` locally. Adding `using System.Reflection;` at the top of `Program.cs` resolves both errors and the build succeeds. This also explains why the forgejo-actions coverage bot has posted **no** comment on this PR — `ci.yml` posts coverage only on a successful build+test, and the build is failing. **Suggested fix:** add `using System.Reflection;` to the top of `Program.cs`, or change the calls to use the static `Attribute.GetCustomAttribute(assembly, typeof(...))` form. **2. MinVer version string contains `+`, which is invalid in a Docker image tag** `.github/workflows/docker-publish.yml:55` — `git.kagaku.eu/teamai/doujin-manager:${{ steps.version.outputs.version }}` MinVer's pre-release output format is `0.1.0-dev.N+sha` (e.g. `0.1.0-dev.5+abc1234`). The `+` separator introduces SemVer build metadata and is **not allowed** in a Docker reference tag — valid tag characters are `[a-z0-9_.-]` only (must also be lowercase, see finding below re: the prior PR #12). `docker buildx` / the registry will reject a tag containing `+`. This means the `build-and-push` job will fail the moment MinVer emits a build-metadata segment (which is the *default* behavior — every non-release commit gets `+sha`). **Suggested fix:** strip/sanitize the version before using it as a Docker tag. Either set `MinVerBuildMetadata` to empty (suppresses the `+sha`), or in the workflow step replace `+` with `-` (and `-` already being valid): ```yaml VERSION=$(minver ... | sed 's/+/-/') ``` The PR's own example (`0.1.0-dev.5+abc1234`) demonstrates the problematic character is always present for dev builds. ### Minor (non-blocking) **3. `MinVerDefaultPreReleasePhase` is deprecated in MinVer 6** `backend/Directory.Build.props:7` — MinVer warns: ``` MINVER1008: MinVerDefaultPreReleasePhase is deprecated and will be removed in the next major version. Use MinVerDefaultPreReleaseIdentifiers instead, with an additional "0" identifier (e.g. "preview.0" instead of "preview"). ``` Since this is a brand-new adoption, worth using the non-deprecated `MinVerDefaultPreReleaseIdentifiers` = `dev.0` property now to avoid a future break. **4. `/version` endpoint duplicates the reflection logic from the startup log** `backend/src/DoujinManager.Server/Program.cs:92-97` (startup) and `:130-141` (`/version`) both re-fetch the `AssemblyInformationalVersionAttribute` inline. Consider extracting a small `static string GetVersion()` helper (or reading it once into a variable/registered singleton) to avoid the duplicated reflection and keep a single source of truth. **5. `.dockerignore` `.git/` un-exclusion increases build context size** `.dockerignore:8` — Removing `.git/` from the ignore list is correct and necessary for MinVer, but the `.git` directory can be large for long-lived repos. Worth a note that the build context will now carry full git history on every `docker build`. Not actionable now, just flagging for awareness. ### Verification - **CI/CD:** No forgejo-actions coverage comment exists on this PR (0 comments), which is consistent with a failing build (ci.yml posts coverage only on success). Per CI-absent/failed policy, I ran local verification: - `dotnet build src/DoujinManager.Server/DoujinManager.Server.csproj -c Release` → **FAILED** (2 CS1061 errors, both `GetCustomAttribute` — see finding #1). Confirmed resolved by adding `using System.Reflection;`. - Diff computed locally via `git diff` between base `5014af3` and head `2ff1b8e`. - Static security scan: clean on added lines (no secrets, shell injection, eval/exec, pickle, or SQL formatting). *Note: This is a conversation comment, not a formal Forgejo review approval — the current MCP integration cannot create review approval states or inline diff comments.* *Automated daily review. I never merge PRs.*
fix: Docker tag +sha sanitization + extract GetInformationalVersion helper
Some checks failed
CI / build (pull_request) Failing after 13s
CI / test (pull_request) Has been skipped
109926e5af
Address automated review feedback:
- #2 (blocker): Docker tags don't allow '+'. Sanitize minver output
  by replacing '+' with '-' (e.g. 0.1.0-dev.5-abc1234 instead of +abc1234)
- #4: Extract GetInformationalVersion() static helper — single source of
  truth for the version string, used by both startup log and /version endpoint
- #1 and #3 already resolved in previous commit (using System.Reflection +
  MinVerDefaultPreReleaseIdentifiers)
Author
Member

All five findings addressed:

#1 (blocker) — Build break — Already fixed in 8c22d71. Added using System.Reflection;.

#2 (blocker) — Docker tag + invalid — Fixed in 109926e. The workflow now sanitizes the MinVer output: VERSION="${VERSION/+/-}". Dev builds get tags like 0.1.0-dev.5-abc1234 instead of +abc1234. Release builds are unaffected (no + in clean versions).

#3 (minor) — MinVer deprecation — Already fixed in 8c22d71. Switched to MinVerDefaultPreReleaseIdentifiers = dev.0.

#4 (minor) — Duplicated reflection logic — Fixed in 109926e. Extracted to a static string GetInformationalVersion() helper. Both the startup log and /version endpoint call the same method — single source of truth.

#5 (minor) — .git in build context — Acknowledged, no action needed. The repo is small and the build cache layers handle this efficiently.

All five findings addressed: **#1 (blocker) — Build break** — Already fixed in `8c22d71`. Added `using System.Reflection;`. **#2 (blocker) — Docker tag `+` invalid** — Fixed in `109926e`. The workflow now sanitizes the MinVer output: `VERSION="${VERSION/+/-}"`. Dev builds get tags like `0.1.0-dev.5-abc1234` instead of `+abc1234`. Release builds are unaffected (no `+` in clean versions). **#3 (minor) — MinVer deprecation** — Already fixed in `8c22d71`. Switched to `MinVerDefaultPreReleaseIdentifiers` = `dev.0`. **#4 (minor) — Duplicated reflection logic** — Fixed in `109926e`. Extracted to a `static string GetInformationalVersion()` helper. Both the startup log and `/version` endpoint call the same method — single source of truth. **#5 (minor) — `.git` in build context** — Acknowledged, no action needed. The repo is small and the build cache layers handle this efficiently.
matikane force-pushed feature/minver-versioning from 109926e5af
Some checks failed
CI / build (pull_request) Failing after 13s
CI / test (pull_request) Has been skipped
to c9cf499cb7
All checks were successful
CI / build (pull_request) Successful in 17s
CI / test (pull_request) Successful in 39s
2026-06-29 20:11:33 +02:00
Compare
Author
Member

🤖 Hermes automated review: no blocking issues found (follow-up)

Re-reviewed the 3 fix commits since the prior review (head 2ff1b8ec9cf499). All 5 prior findings are resolved. The force-update replaced the broken head with a clean, building set of commits.

Prior findings — resolution verification

# Finding Status Fix commit Verification
1 Build break: missing using System.Reflection; (CS1061) Fixed 1cc8df7 using System.Reflection; added at backend/src/DoujinManager.Server/Program.cs:16; GetCustomAttribute<> now resolves. Confirmed by CI (coverage comment #356 posted for head c9cf499, and ci.yml posts coverage only on successful build+test).
2 Docker tag +sha invalid Fixed cb7d33c .github/workflows/docker-publish.yml:28 now runs VERSION="${VERSION/+/-}" after minver. I verified the substitution: 0.1.0-dev.5+abc12340.1.0-dev.5-abc1234, and clean versions (0.1.0) are untouched. - is a valid Docker tag char.
3 MinVerDefaultPreReleasePhase deprecated Fixed 1cc8df7 backend/Directory.Build.props:6 now uses <MinVerDefaultPreReleaseIdentifiers>dev.0</MinVerDefaultPreReleaseIdentifiers> (the non-deprecated MinVer 6 property).
4 Duplicated reflection logic Fixed cb7d33c + c9cf499 Extracted to static string GetInformationalVersion() (Program.cs:148), called by both the startup log and /version endpoint. Single source of truth. c9cf499 additionally moved the method above the top-level-statement boundary to fix a CS8803 warning.
5 .git in build context Acknowledged n/a Author acknowledged; no action needed (small repo).

Static security scan

Clean on the 3 fix commits (added lines only): no secrets, no shell injection, no eval/exec, no pickle, no SQL string formatting.

Verification

  • CI/CD: forgejo-actions coverage comment #356 is current for head c9cf499 (coverage date 06/29 18:12:15–18:12:29 UTC, generated 18:12:31 UTC; head commit c9cf499 timestamp 20:11 per PR). Line coverage 83.6%, branch 49.5%. Since ci.yml posts coverage only on a successful build+test, this confirms the build break is fixed and the suite passes. Local build/test skipped per CI-evidence policy.
  • Diff computed locally via git diff 2ff1b8e...c9cf499 on the 3 fix commits, plus full-range 7907427...c9cf499 for context.
  • One note: the docker-publish.yml workflow triggers on push:main and v* tags only — not on pull_request. So the container-tag sanitization is not exercised by PR CI; it will first run after merge / on tag push. This is a pre-existing workflow-design choice (consistent with PR #22's path filters), not a regression introduced here.

Verdict

No blocking or minor issues remain. This is a clean follow-up.

Note: This is a conversation comment, not a formal Forgejo review approval — the current MCP integration cannot create review approval states or inline diff comments.

Automated daily review. I never merge PRs.

## 🤖 Hermes automated review: no blocking issues found (follow-up) Re-reviewed the 3 fix commits since the prior review (head `2ff1b8e` → `c9cf499`). All 5 prior findings are resolved. The force-update replaced the broken head with a clean, building set of commits. ### Prior findings — resolution verification | # | Finding | Status | Fix commit | Verification | |---|---------|--------|-----------|--------------| | 1 | Build break: missing `using System.Reflection;` (CS1061) | ✅ Fixed | `1cc8df7` | `using System.Reflection;` added at `backend/src/DoujinManager.Server/Program.cs:16`; `GetCustomAttribute<>` now resolves. **Confirmed by CI** (coverage comment #356 posted for head `c9cf499`, and `ci.yml` posts coverage only on successful build+test). | | 2 | Docker tag `+sha` invalid | ✅ Fixed | `cb7d33c` | `.github/workflows/docker-publish.yml:28` now runs `VERSION="${VERSION/+/-}"` after `minver`. I verified the substitution: `0.1.0-dev.5+abc1234` → `0.1.0-dev.5-abc1234`, and clean versions (`0.1.0`) are untouched. `-` is a valid Docker tag char. | | 3 | `MinVerDefaultPreReleasePhase` deprecated | ✅ Fixed | `1cc8df7` | `backend/Directory.Build.props:6` now uses `<MinVerDefaultPreReleaseIdentifiers>dev.0</MinVerDefaultPreReleaseIdentifiers>` (the non-deprecated MinVer 6 property). | | 4 | Duplicated reflection logic | ✅ Fixed | `cb7d33c` + `c9cf499` | Extracted to `static string GetInformationalVersion()` (`Program.cs:148`), called by both the startup log and `/version` endpoint. Single source of truth. `c9cf499` additionally moved the method above the top-level-statement boundary to fix a CS8803 warning. | | 5 | `.git` in build context | ✅ Acknowledged | n/a | Author acknowledged; no action needed (small repo). | ### Static security scan Clean on the 3 fix commits (added lines only): no secrets, no shell injection, no `eval`/`exec`, no `pickle`, no SQL string formatting. ### Verification - **CI/CD:** forgejo-actions coverage comment **#356** is **current for head `c9cf499`** (coverage date 06/29 18:12:15–18:12:29 UTC, generated 18:12:31 UTC; head commit `c9cf499` timestamp 20:11 per PR). Line coverage 83.6%, branch 49.5%. Since `ci.yml` posts coverage only on a successful build+test, this confirms the build break is fixed and the suite passes. **Local build/test skipped per CI-evidence policy.** - Diff computed locally via `git diff 2ff1b8e...c9cf499` on the 3 fix commits, plus full-range `7907427...c9cf499` for context. - One note: the `docker-publish.yml` workflow triggers on `push:main` and `v*` tags only — **not** on `pull_request`. So the container-tag sanitization is not exercised by PR CI; it will first run after merge / on tag push. This is a pre-existing workflow-design choice (consistent with PR #22's path filters), not a regression introduced here. ### Verdict No blocking or minor issues remain. This is a clean follow-up. *Note: This is a conversation comment, not a formal Forgejo review approval — the current MCP integration cannot create review approval states or inline diff comments.* *Automated daily review. I never merge PRs.*
bjoern merged commit 5a636d6e8a into main 2026-06-29 20:19:04 +02:00
bjoern deleted branch feature/minver-versioning 2026-06-29 20:19:04 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
TeamAI/doujin-manager!23
No description provided.