feat: CI registry build+push, Portainer web-editor deploy #12

Merged
bjoern merged 3 commits from feat/registry-build-push into main 2026-06-29 01:59:48 +02:00
Member

Push-to-deploy via Forgejo container registry

Sidesteps the clone-on-NAS problem entirely. The CI builds and pushes the image to the built-in Forgejo registry on every push to main. Portainer pulls the pre-built image — no Dockerfile, no repo clone on the NAS.

What changed

New: .github/workflows/docker-publish.yml — runs on push to main:

  • Builds the Docker image using the existing deploy/Dockerfile
  • Pushes to git.kagaku.eu/teamai/doujin-manager:latest and :{commit-sha}
  • Uses GITHUB_TOKEN for registry auth (auto-provided by Forgejo Actions, packages: write permission)
  • runs-on: docker (needs a runner with Docker daemon access)

Rewritten: deploy/docker-compose.yml — Portainer Web Editor ready:

  • image: git.kagaku.eu/teamai/doujin-manager:latest (no build: line)
  • Inline DOUJIN_MANAGER_AUTH_TOKEN (user replaces in Portainer editor)
  • External volumes preserved (doujinshi_images, doujinshi_thumbnails, doujinshi_backup)

Rewritten: deploy/DEPLOYMENT.md:

  • Section 4: Portainer Web Editor quick start (paste compose, set token, deploy)
  • Section 4.4: Redeploy = Update the stack (pulls new :latest)
  • Section 5: Manual build/push alternative for testing branches
  • Removed Synology Container Manager section (superseded by Portainer approach)

Deploy flow

git push main → CI builds image → pushes to Forgejo registry
→ Portainer: Update the stack → pulls :latest → container recreated

No cloning on the NAS. No build context issues. Fully self-hosted.

## Push-to-deploy via Forgejo container registry Sidesteps the clone-on-NAS problem entirely. The CI builds and pushes the image to the built-in Forgejo registry on every push to main. Portainer pulls the pre-built image — no Dockerfile, no repo clone on the NAS. ### What changed **New: `.github/workflows/docker-publish.yml`** — runs on push to main: - Builds the Docker image using the existing `deploy/Dockerfile` - Pushes to `git.kagaku.eu/teamai/doujin-manager:latest` and `:{commit-sha}` - Uses `GITHUB_TOKEN` for registry auth (auto-provided by Forgejo Actions, `packages: write` permission) - `runs-on: docker` (needs a runner with Docker daemon access) **Rewritten: `deploy/docker-compose.yml`** — Portainer Web Editor ready: - `image: git.kagaku.eu/teamai/doujin-manager:latest` (no `build:` line) - Inline `DOUJIN_MANAGER_AUTH_TOKEN` (user replaces in Portainer editor) - External volumes preserved (`doujinshi_images`, `doujinshi_thumbnails`, `doujinshi_backup`) **Rewritten: `deploy/DEPLOYMENT.md`**: - Section 4: Portainer Web Editor quick start (paste compose, set token, deploy) - Section 4.4: Redeploy = Update the stack (pulls new `:latest`) - Section 5: Manual build/push alternative for testing branches - Removed Synology Container Manager section (superseded by Portainer approach) ### Deploy flow ``` git push main → CI builds image → pushes to Forgejo registry → Portainer: Update the stack → pulls :latest → container recreated ``` No cloning on the NAS. No build context issues. Fully self-hosted.
feat: CI builds and pushes image to Forgejo registry, Portainer deploy
All checks were successful
CI / build (pull_request) Successful in 17s
CI / test (pull_request) Successful in 39s
7eafbfd27a
Add docker-publish.yml workflow that builds and pushes the Docker image
to git.kagaku.eu/teamai/doujin-manager:latest on every push to main.
Uses the GITHUB_TOKEN for registry auth (packages: write permission).

Rewrite docker-compose.yml for Portainer Web Editor usage:
- No build: line — just image: git.kagaku.eu/teamai/doujin-manager:latest
- Inline auth token (replaced by user in Portainer editor)
- External volumes preserved

Rewrite DEPLOYMENT.md:
- Section 4: Portainer Web Editor quick start (no cloning/building on NAS)
- Section 4.4: Redeploy = Update the stack in Portainer
- Section 5: Manual build/push alternative
- Section 8: Update procedure via Portainer
- Removed Synology Container Manager section (superseded by Portainer)
- Removed docker-compose --env-file instructions

Summary

Summary
Generated on: 06/28/2026 - 23:59:04
Coverage date: 06/28/2026 - 23:58:49 - 06/28/2026 - 23:59:02
Parser: MultiReport (4x Cobertura)
Assemblies: 4
Classes: 206
Files: 92
Line coverage: 84% (4199 of 4998)
Covered lines: 4199
Uncovered lines: 799
Coverable lines: 4998
Total lines: 8631
Branch coverage: 50.2% (369 of 734)
Covered branches: 369
Total branches: 734
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.7%
Name Line Branch
DoujinManager.Infrastructure 91.7% 71.2%
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 84.4% 75%
DoujinManager.Infrastructure.Services.ChapterService 54.2% 25%
DoujinManager.Infrastructure.Services.CircleService 69.6% 0%
DoujinManager.Infrastructure.Services.DoujinService 69.5% 45.8%
DoujinManager.Infrastructure.Services.ImageService 91% 50%
DoujinManager.Infrastructure.Services.PageService 84.9% 65%
DoujinManager.Infrastructure.Services.PersonService 69.6% 0%
DoujinManager.Infrastructure.Services.SearchService 100% 100%
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% 75%
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 90.9% 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 92.8% 92.8%
DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase 74.1% 62.5%
DoujinManager.RestAdapter - 83.7%
Name Line Branch
DoujinManager.RestAdapter 83.7% 76.4%
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.CreateBackupResponse 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% 87.5%
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 86.1% 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.RestAdapterExtensions 100% 100%
Microsoft.Extensions.Validation.Generated 78.6% 82.6%
Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C
E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr
ibuteCache
100% 87.5%
System.Runtime.CompilerServices 0%
DoujinManager.Server - 18.4%
Name Line Branch
DoujinManager.Server 18.4% 0%
DoujinManager.Server.ImageInfrastructureRegistration 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/28/2026 - 23:59:04 | | Coverage date: | 06/28/2026 - 23:58:49 - 06/28/2026 - 23:59:02 | | Parser: | MultiReport (4x Cobertura) | | Assemblies: | 4 | | Classes: | 206 | | Files: | 92 | | **Line coverage:** | 84% (4199 of 4998) | | Covered lines: | 4199 | | Uncovered lines: | 799 | | Coverable lines: | 4998 | | Total lines: | 8631 | | **Branch coverage:** | 50.2% (369 of 734) | | Covered branches: | 369 | | Total branches: | 734 | | **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.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Infrastructure**|**91.7%**|**71.2%**| |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|84.4%|75%| |DoujinManager.Infrastructure.Services.ChapterService|54.2%|25%| |DoujinManager.Infrastructure.Services.CircleService|69.6%|0%| |DoujinManager.Infrastructure.Services.DoujinService|69.5%|45.8%| |DoujinManager.Infrastructure.Services.ImageService|91%|50%| |DoujinManager.Infrastructure.Services.PageService|84.9%|65%| |DoujinManager.Infrastructure.Services.PersonService|69.6%|0%| |DoujinManager.Infrastructure.Services.SearchService|100%|100%| |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%|75%| |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|90.9%|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|92.8%|92.8%| |DoujinManager.Infrastructure.UseCases.UploadZipPagesUseCase|74.1%|62.5%| </details> <details><summary>DoujinManager.RestAdapter - 83.7%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.RestAdapter**|**83.7%**|**76.4%**| |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.CreateBackupResponse|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%|87.5%| |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|86.1%|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.RestAdapterExtensions|100%|100%| |Microsoft.Extensions.Validation.Generated|78.6%|82.6%| |Microsoft.Extensions.Validation.Generated.<ValidatableInfoResolver_g>FB9B0C<br/>E5CB12CEAC5BBBEA65844E1C05F9ADA4DA38B848075C41304FC6A1ABF77__ValidationAttr<br/>ibuteCache|100%|87.5%| |System.Runtime.CompilerServices|0%|| </details> <details><summary>DoujinManager.Server - 18.4%</summary> |**Name**|**Line**|**Branch**| |:---|---:|---:| |**DoujinManager.Server**|**18.4%**|**0%**| |DoujinManager.Server.ImageInfrastructureRegistration|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 the diff for head 7eafbfd2 (base f9ba64c3, 3 files, +91/-113). Static security scan on added lines: clean (no secrets, shell injection, eval/exec, or SQL injection). This is a deploy/CI-only change (no C# application code), so the existing ci.yml build+test coverage applies.

Findings

  • [major] .github/workflows/docker-publish.yml:33-34 — image tag will be rejected by Docker (uppercase repository name).
    ${{ github.repository }} for this repo is TeamAI/doujin-manager (uppercase T, A, I). Docker image references forbid uppercase characters in the repository path, so the "Build and push" step produces a tag like git.kagaku.eu/TeamAI/doujin-manager:latest, which buildx/distribution rejects with invalid reference format / repository name must be lowercase. The very first push to main will fail the workflow at this step.
    Suggested fix — lowercase the namespace. Either pass it through an env var in a prior step:

    - name: Lowercase image name
      run: echo "IMAGE=${{ github.server_url }}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
    

    and then use tags: ${{ env.IMAGE }}:latest / ${{ env.IMAGE }}:${{ github.sha }}, or hardcode the lowercase path (git.kagaku.eu/teamai/doujin-manager).

  • [major] Push path vs. pull path mismatch — Portainer will not find the image.
    deploy/docker-compose.yml:24 pulls git.kagaku.eu/teamai/doujin-manager:latest (lowercase), but after fixing the tag above, what actually gets pushed is whatever the registry stores. The Forgejo container registry stores packages under the owner/path as-pushed, and the two must be byte-identical. Make sure the workflow's lowercased push path and the compose pull path are the same string. Today they differ (TeamAI/... push vs teamai/... pull) — reconcile both to lowercase.

  • [minor] docker/login-action registry: likely rejects the scheme prefix. login-action@v3 (and the registry client) generally expect a bare host, but ${{ github.server_url }} expands to https://git.kagaku.eu. Passing https://git.kagaku.eu as registry: can fail with a URL-parse error. Consider deriving a scheme-less host (e.g. echo "REGISTRY=${{ github.server_url }}" | sed 's#^https\?://##'). Worth a quick check against your runner.

Non-blocking notes

  • Inline DOUJIN_MANAGER_AUTH_TOKEN=replac...oken placeholder in docker-compose.yml is fine for a Portainer-paste workflow; just ensure the real token is only ever entered in Portainer (which encrypts stack env vars) and never committed. The previous ${DOUJIN...must be set} form was equally safe.
  • runs-on: docker requires a self-hosted runner with a Docker daemon — your ci.yml already uses runs-on: dotnet, so confirm a docker-labeled runner exists, otherwise the publish job will stay queued.
  • No PR-triggered smoke build of the Dockerfile here (workflow only runs on push: main). Acceptable since the Dockerfile build path is exercised implicitly and ci.yml already builds the .NET app — but if you want a pre-merge signal, adding pull_request: to the triggers (without push: true) is cheap.

Verification

  • CI/CD: Forgejo Actions coverage comment #249 is current for head 7eafbfd2 (line 84%, branch 50.2%, generated 06/29 01:39). The ci.yml test job only posts coverage on success, so build+test passed for this head. Local build/test skipped per CI-evidence policy (and this PR contains no application code).
  • The new docker-publish.yml does not run on pull_request, so its correctness could not be CI-verified here — the uppercase-tag issue above would only surface on the first post-merge push to main.

Automated daily review. I never merge PRs.

## 🤖 Hermes automated review: changes requested Reviewed the diff for head `7eafbfd2` (base `f9ba64c3`, 3 files, +91/-113). Static security scan on added lines: **clean** (no secrets, shell injection, eval/exec, or SQL injection). This is a deploy/CI-only change (no C# application code), so the existing `ci.yml` build+test coverage applies. ### Findings - **[major] `.github/workflows/docker-publish.yml:33-34` — image tag will be rejected by Docker (uppercase repository name).** `${{ github.repository }}` for this repo is `TeamAI/doujin-manager` (uppercase `T`, `A`, `I`). Docker image references forbid uppercase characters in the repository path, so the "Build and push" step produces a tag like `git.kagaku.eu/TeamAI/doujin-manager:latest`, which buildx/distribution rejects with `invalid reference format` / `repository name must be lowercase`. The very first push to `main` will fail the workflow at this step. **Suggested fix** — lowercase the namespace. Either pass it through an env var in a prior step: ```yaml - name: Lowercase image name run: echo "IMAGE=${{ github.server_url }}/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" ``` and then use `tags: ${{ env.IMAGE }}:latest` / `${{ env.IMAGE }}:${{ github.sha }}`, or hardcode the lowercase path (`git.kagaku.eu/teamai/doujin-manager`). - **[major] Push path vs. pull path mismatch — Portainer will not find the image.** `deploy/docker-compose.yml:24` pulls `git.kagaku.eu/teamai/doujin-manager:latest` (lowercase), but after fixing the tag above, what actually gets pushed is whatever the registry stores. The Forgejo container registry stores packages under the owner/path as-pushed, and the two must be byte-identical. Make sure the workflow's lowercased push path and the compose pull path are the **same** string. Today they differ (`TeamAI/...` push vs `teamai/...` pull) — reconcile both to lowercase. - **[minor] `docker/login-action` `registry:` likely rejects the scheme prefix.** `login-action@v3` (and the registry client) generally expect a bare host, but `${{ github.server_url }}` expands to `https://git.kagaku.eu`. Passing `https://git.kagaku.eu` as `registry:` can fail with a URL-parse error. Consider deriving a scheme-less host (e.g. `echo "REGISTRY=${{ github.server_url }}" | sed 's#^https\?://##'`). Worth a quick check against your runner. ### Non-blocking notes - Inline `DOUJIN_MANAGER_AUTH_TOKEN=replac...oken` placeholder in `docker-compose.yml` is fine for a Portainer-paste workflow; just ensure the real token is only ever entered in Portainer (which encrypts stack env vars) and never committed. The previous `${DOUJIN...must be set}` form was equally safe. - `runs-on: docker` requires a self-hosted runner with a Docker daemon — your `ci.yml` already uses `runs-on: dotnet`, so confirm a `docker`-labeled runner exists, otherwise the publish job will stay queued. - No PR-triggered smoke build of the Dockerfile here (workflow only runs on `push: main`). Acceptable since the Dockerfile build path is exercised implicitly and `ci.yml` already builds the .NET app — but if you want a pre-merge signal, adding `pull_request:` to the triggers (without `push: true`) is cheap. ### Verification - **CI/CD:** Forgejo Actions coverage comment #249 is current for head `7eafbfd2` (line 84%, branch 50.2%, generated 06/29 01:39). The `ci.yml` `test` job only posts coverage on success, so build+test passed for this head. **Local build/test skipped per CI-evidence policy** (and this PR contains no application code). - The new `docker-publish.yml` does **not** run on `pull_request`, so its correctness could not be CI-verified here — the uppercase-tag issue above would only surface on the first post-merge push to `main`. *Automated daily review. I never merge PRs.*
fix: lowercase image tags, bare registry host for login
All checks were successful
CI / build (pull_request) Successful in 18s
CI / test (pull_request) Successful in 37s
b5663eb4b5
- Hardcode lowercase git.kagaku.eu/teamai/doujin-manager:latest (Docker
  rejects uppercase in image refs; github.repository is TeamAI/...)
- Use bare host 'git.kagaku.eu' for login-action registry (not the
  https://-prefixed github.server_url which causes URL parse errors)
- Push path and compose pull path now match exactly
Author
Member

Fixed all 3 findings in commit b5663eb.

#1 — Uppercase image tag
Hardcoded git.kagaku.eu/teamai/doujin-manager (lowercase) instead of ${{ github.repository }} which expands to TeamAI/doujin-manager. Docker rejects uppercase in image references.

#2 — Push/pull path mismatch
The workflow now pushes to exactly git.kagaku.eu/teamai/doujin-manager:latest — the same string the compose file pulls. Byte-identical.

#3 — Registry scheme prefix
Changed registry: from ${{ github.server_url }} (which expands to https://git.kagaku.eu) to bare git.kagaku.eu. docker/login-action expects a host, not a URL.

Fixed all 3 findings in commit `b5663eb`. **#1 — Uppercase image tag** ✅ Hardcoded `git.kagaku.eu/teamai/doujin-manager` (lowercase) instead of `${{ github.repository }}` which expands to `TeamAI/doujin-manager`. Docker rejects uppercase in image references. **#2 — Push/pull path mismatch** ✅ The workflow now pushes to exactly `git.kagaku.eu/teamai/doujin-manager:latest` — the same string the compose file pulls. Byte-identical. **#3 — Registry scheme prefix** ✅ Changed `registry:` from `${{ github.server_url }}` (which expands to `https://git.kagaku.eu`) to bare `git.kagaku.eu`. `docker/login-action` expects a host, not a URL.
fix(ci): push with REGISTRY_TOKEN secret (GITHUB_TOKEN lacks package write)
All checks were successful
CI / build (pull_request) Successful in 22s
CI / test (pull_request) Successful in 37s
5283079e70
bjoern merged commit 1ad688974f into main 2026-06-29 01:59:48 +02:00
bjoern deleted branch feat/registry-build-push 2026-06-29 01:59:48 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 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!12
No description provided.