From 9a2d88b93b617acef387d1a9495a0f5da7e9fd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96ZT=C3=9CRK?= <76204082+iamsedatozturk@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:51:46 +0300 Subject: [PATCH] ReleaseNote -> ChangeLog --- .gitignore | 4 +- README.md | 14 +- .../ChangeLogDto.cs} | 4 +- .../ChangeLogAppService.cs} | 18 +-- .../Enums/TableNameEnum.cs | 2 +- .../TableNameResolver.cs | 2 +- .../Host/{ReleaseNote.cs => ChangeLog.cs} | 8 +- .../EntityFrameworkCore/PlatformDbContext.cs | 6 +- ....cs => 20260817083025_Initial.Designer.cs} | 126 +++++++++--------- ...2_Initial.cs => 20260817083025_Initial.cs} | 66 ++++----- .../PlatformDbContextModelSnapshot.cs | 124 ++++++++--------- ...pService.cs => ChangeLogStartupService.cs} | 42 +++--- ...formApplicationConfigurationContributor.cs | 8 +- .../PlatformHttpApiHostModule.cs | 4 +- .../Sozsoft.Platform.HttpApi.Host.csproj | 4 +- configs/change-logs/README.md | 18 +++ .../generate-change-logs.sh} | 12 +- configs/deployment/docker-compose-app.yml | 4 +- configs/deployment/scripts/build/api.sh | 2 +- configs/deployment/scripts/deploy/app.sh | 8 +- configs/release-notes/README.md | 18 --- ...seNote.service.ts => changeLog.service.ts} | 14 +- ui/src/views/version/ChangeLog.tsx | 8 +- 23 files changed, 258 insertions(+), 258 deletions(-) rename api/src/Sozsoft.Platform.Application.Contracts/{ReleaseNotes/ReleaseNoteDto.cs => ChangeLogs/ChangeLogDto.cs} (75%) rename api/src/Sozsoft.Platform.Application/{ReleaseNotes/ReleaseNoteAppService.cs => ChangeLog/ChangeLogAppService.cs} (61%) rename api/src/Sozsoft.Platform.Domain/Entities/Host/{ReleaseNote.cs => ChangeLog.cs} (86%) rename api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/{20260812194642_Initial.Designer.cs => 20260817083025_Initial.Designer.cs} (99%) rename api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/{20260812194642_Initial.cs => 20260817083025_Initial.cs} (99%) rename api/src/Sozsoft.Platform.HttpApi.Host/Extensions/{ReleaseNoteStartupService.cs => ChangeLogStartupService.cs} (87%) create mode 100644 configs/change-logs/README.md rename configs/{release-notes/generate-release-notes.sh => change-logs/generate-change-logs.sh} (90%) delete mode 100644 configs/release-notes/README.md rename ui/src/services/{releaseNote.service.ts => changeLog.service.ts} (52%) diff --git a/.gitignore b/.gitignore index fd8aad0e..7eea4de7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ configs/**/data/** **/.DS_Store logs/ -# Deploy sırasında git tag'lerinden üretilir (configs/release-notes/generate-release-notes.sh) +# Deploy sırasında git tag'lerinden üretilir (configs/change-logs/generate-change-logs.sh) # ve API konteynerine mount edilir. Takip edilirse deploy'daki `git pull` yerel # değişiklikle çakışır ve dosya eskir. -configs/release-notes/release-notes.json \ No newline at end of file +configs/change-logs/change-logs.json \ No newline at end of file diff --git a/README.md b/README.md index 69e80b6f..43ccfd0e 100644 --- a/README.md +++ b/README.md @@ -710,23 +710,23 @@ konfigürasyonu ile üretilir. (`views/version/swRegistration.ts`, `sw.ts`, `AppUpdateDialog.tsx`). - Isıtma yalnızca kuruluma bağlı değildir: uygulama açılışta `ENSURE_WARMUP` mesajı yollar, service worker eksik kalan bileşenleri kaldığı yerden tamamlar. -- **Sürüm bilgisi backend'dedir.** `Sas_H_ReleaseNote` tablosu tek kaynaktır; istemci sürüm +- **Sürüm bilgisi backend'dedir.** `Sas_H_ChangeLog` tablosu tek kaynaktır; istemci sürüm numarasını ve son sürümün notlarını `application-configuration` yanıtındaki `extraProperties.changeLogs` alanından okur (`PlatformApplicationConfigurationContributor`), - tam listeyi ise `/api/app/release-note` endpoint'inden alır. Ayrı bir `version.json` isteği + tam listeyi ise `/api/app/change-log` endpoint'inden alır. Ayrı bir `version.json` isteği yoktur. Changelog `/admin/changeLog` ekranında gösterilir; sürüm numarası son görülenden farklıysa kullanıcı giriş sonrası oraya yönlendirilir (tetikleyici bundle değil, sürümdür — yalnızca yeni bir git tag'i eklenmiş olsa da çalışır). -- **Sürüm notu akışı — DbMigrator gerektirmez:** `configs/release-notes/generate-release-notes.sh` - deploy sırasında (`deploy/app.sh`) git tag'lerinden `configs/release-notes/release-notes.json` - üretir, dosya API konteynerine `/etc/api/release-notes` altına salt okunur mount edilir ve - `ReleaseNoteStartupService` uygulama açılışında tabloyu bu dosyayla eşitler. Dosya imaja +- **Sürüm notu akışı — DbMigrator gerektirmez:** `configs/change-logs/generate-change-logs.sh` + deploy sırasında (`deploy/app.sh`) git tag'lerinden `configs/change-logs/change-logs.json` + üretir, dosya API konteynerine `/etc/api/change-logs` altına salt okunur mount edilir ve + `ChangeLogStartupService` uygulama açılışında tabloyu bu dosyayla eşitler. Dosya imaja **gömülmez**: api build context'i içindeyken her yeni tag Dockerfile'daki `COPY . .` katmanını geçersiz kılıp imajı gereksiz yere yeniden derletiyordu; artık kod değişmediyse imaj cache'ten gelir. Yeni sürüm için tag atıp deploy etmek yeterlidir. Notlar **annotated** tag mesajından okunur (`git tag -a 1.2.0 -m "..."`), her satır bir madde. - **Tablo git tag listesinin aynasıdır:** yeni tag eklenir, mesajı düzeltilen tag güncellenir, - git'ten silinen tag tablodan da (hard delete ile) kaldırılır. Bu yüzden `Sas_H_ReleaseNote` + git'ten silinen tag tablodan da (hard delete ile) kaldırılır. Bu yüzden `Sas_H_ChangeLog` elle düzenlenmemelidir — ilk deploy'da geri alınır. Dosya okunamaz veya boşsa senkronizasyon hiçbir şey yapmaz, tablo boşaltılmaz. - Deploy algılaması service worker'ın kendi `registration.update()` kontrolüne dayanır: diff --git a/api/src/Sozsoft.Platform.Application.Contracts/ReleaseNotes/ReleaseNoteDto.cs b/api/src/Sozsoft.Platform.Application.Contracts/ChangeLogs/ChangeLogDto.cs similarity index 75% rename from api/src/Sozsoft.Platform.Application.Contracts/ReleaseNotes/ReleaseNoteDto.cs rename to api/src/Sozsoft.Platform.Application.Contracts/ChangeLogs/ChangeLogDto.cs index 898a19ac..76a6e66c 100644 --- a/api/src/Sozsoft.Platform.Application.Contracts/ReleaseNotes/ReleaseNoteDto.cs +++ b/api/src/Sozsoft.Platform.Application.Contracts/ChangeLogs/ChangeLogDto.cs @@ -1,9 +1,9 @@ using System; using Volo.Abp.Application.Dtos; -namespace Sozsoft.Platform.ReleaseNotes; +namespace Sozsoft.Platform.ChangeLogs; -public class ReleaseNoteDto : EntityDto +public class ChangeLogDto : EntityDto { public string Version { get; set; } public DateTime ReleaseDate { get; set; } diff --git a/api/src/Sozsoft.Platform.Application/ReleaseNotes/ReleaseNoteAppService.cs b/api/src/Sozsoft.Platform.Application/ChangeLog/ChangeLogAppService.cs similarity index 61% rename from api/src/Sozsoft.Platform.Application/ReleaseNotes/ReleaseNoteAppService.cs rename to api/src/Sozsoft.Platform.Application/ChangeLog/ChangeLogAppService.cs index 82966175..f573d9ce 100644 --- a/api/src/Sozsoft.Platform.Application/ReleaseNotes/ReleaseNoteAppService.cs +++ b/api/src/Sozsoft.Platform.Application/ChangeLog/ChangeLogAppService.cs @@ -6,34 +6,34 @@ using Microsoft.AspNetCore.Authorization; using Sozsoft.Platform.Entities; using Volo.Abp.Domain.Repositories; -namespace Sozsoft.Platform.ReleaseNotes; +namespace Sozsoft.Platform.ChangeLogs; /// /// Sürüm notları (ChangeLog ekranının kaynağı). /// -/// Kayıtlar git tag'lerinden üretilip Sas_H_ReleaseNote tablosuna +/// Kayıtlar git tag'lerinden üretilip Sas_H_ChangeLog tablosuna /// tohumlanır. Uygulamanın çalışan sürüm numarası ise application-configuration /// yanıtındaki extraProperties.changeLogs ile gelir; bu servis yalnızca /// tam listeyi döner, böylece her config isteğinde tüm changelog taşınmaz. /// [Authorize] -public class ReleaseNoteAppService : PlatformAppService +public class ChangeLogAppService : PlatformAppService { - private readonly IRepository _releaseNoteRepository; + private readonly IRepository _changeLogRepository; - public ReleaseNoteAppService(IRepository releaseNoteRepository) + public ChangeLogAppService(IRepository changeLogRepository) { - _releaseNoteRepository = releaseNoteRepository; + _changeLogRepository = changeLogRepository; } - public async Task> GetListAsync() + public async Task> GetListAsync() { - var items = await _releaseNoteRepository.GetListAsync(x => x.IsPublished); + var items = await _changeLogRepository.GetListAsync(x => x.IsPublished); return items .OrderByDescending(note => note.ReleaseDate) .ThenByDescending(note => note.Version) - .Select(note => new ReleaseNoteDto + .Select(note => new ChangeLogDto { Id = note.Id, Version = note.Version, diff --git a/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs b/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs index 0fa3e125..fdc744d2 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/Enums/TableNameEnum.cs @@ -98,5 +98,5 @@ public enum TableNameEnum DbTableColumn, DevRequest, Agenda, - ReleaseNote + ChangeLog } diff --git a/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs b/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs index b08469f4..9747a873 100644 --- a/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs +++ b/api/src/Sozsoft.Platform.Domain.Shared/TableNameResolver.cs @@ -15,7 +15,7 @@ public static class TableNameResolver { nameof(TableNameEnum.DbTableColumn), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.LogEntry), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.AiBot), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, - { nameof(TableNameEnum.ReleaseNote), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, + { nameof(TableNameEnum.ChangeLog), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.GlobalSearch), (TablePrefix.TenantByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.ContactTitle), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, { nameof(TableNameEnum.Currency), (TablePrefix.PlatformByName, MenuPrefix.Saas) }, diff --git a/api/src/Sozsoft.Platform.Domain/Entities/Host/ReleaseNote.cs b/api/src/Sozsoft.Platform.Domain/Entities/Host/ChangeLog.cs similarity index 86% rename from api/src/Sozsoft.Platform.Domain/Entities/Host/ReleaseNote.cs rename to api/src/Sozsoft.Platform.Domain/Entities/Host/ChangeLog.cs index dabce4d0..6a3d3082 100644 --- a/api/src/Sozsoft.Platform.Domain/Entities/Host/ReleaseNote.cs +++ b/api/src/Sozsoft.Platform.Domain/Entities/Host/ChangeLog.cs @@ -9,12 +9,12 @@ namespace Sozsoft.Platform.Entities; /// changelog'u application-configuration yanıtındaki /// extraProperties.changeLogs alanından okur, ayrı bir sürüm dosyası yoktur. /// -/// Kayıtlar git tag'lerinden türer ve açılışta ReleaseNoteStartupService +/// Kayıtlar git tag'lerinden türer ve açılışta ChangeLogStartupService /// tarafından tag listesiyle birebir eşitlenir. /// /// Host seviyesindedir (tenant'a göre değişmez). /// -public class ReleaseNote : FullAuditedEntity +public class ChangeLog : FullAuditedEntity { /// Kullanıcıya gösterilen sürüm numarası (ör. "1.2.1"). public string Version { get; set; } @@ -46,11 +46,11 @@ public class ReleaseNote : FullAuditedEntity .ToArray(); } - protected ReleaseNote() + protected ChangeLog() { } - public ReleaseNote(string version, DateTime releaseDate, string notes, string commit = null, bool isPublished = true) + public ChangeLog(string version, DateTime releaseDate, string notes, string commit = null, bool isPublished = true) { Version = version; ReleaseDate = releaseDate; diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs index eb874c85..1565b4e1 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/EntityFrameworkCore/PlatformDbContext.cs @@ -46,7 +46,7 @@ public class PlatformDbContext : public DbSet Tenants { get; set; } public DbSet TenantConnectionStrings { get; set; } public DbSet AiBots { get; set; } - public DbSet ReleaseNotes { get; set; } + public DbSet ChangeLogs { get; set; } public DbSet Branches { get; set; } public DbSet BranchUsers { get; set; } public DbSet GlobalSearchs { get; set; } @@ -203,9 +203,9 @@ public class PlatformDbContext : b.HasIndex(x => new { x.Name }).IsUnique().HasFilter(null); }); - builder.Entity(b => + builder.Entity(b => { - b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ReleaseNote)), Prefix.DbSchema); + b.ToTable(TableNameResolver.GetFullTableName(nameof(TableNameEnum.ChangeLog)), Prefix.DbSchema); b.ConfigureByConvention(); b.Property(a => a.Version).IsRequired().HasMaxLength(32); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260812194642_Initial.Designer.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260817083025_Initial.Designer.cs similarity index 99% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260812194642_Initial.Designer.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260817083025_Initial.Designer.cs index e2ac5704..5d8beff3 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260812194642_Initial.Designer.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260817083025_Initial.Designer.cs @@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore; namespace Sozsoft.Platform.Migrations { [DbContext(typeof(PlatformDbContext))] - [Migration("20260812194642_Initial")] + [Migration("20260817083025_Initial")] partial class Initial { /// @@ -1381,6 +1381,68 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_T_BranchUsers", (string)null); }); + modelBuilder.Entity("Sozsoft.Platform.Entities.ChangeLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Commit") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.HasKey("Id"); + + b.HasIndex("Version") + .IsUnique() + .HasFilter("[IsDeleted] = 0"); + + b.ToTable("Sas_H_ChangeLog", (string)null); + }); + modelBuilder.Entity("Sozsoft.Platform.Entities.City", b => { b.Property("Id") @@ -4662,68 +4724,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_H_Product", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.ReleaseNote", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Commit") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("IsPublished") - .HasColumnType("bit"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Notes") - .HasColumnType("nvarchar(max)"); - - b.Property("ReleaseDate") - .HasColumnType("datetime2"); - - b.Property("Version") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("nvarchar(32)"); - - b.HasKey("Id"); - - b.HasIndex("Version") - .IsUnique() - .HasFilter("[IsDeleted] = 0"); - - b.ToTable("Sas_H_ReleaseNote", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.ReportCategory", b => { b.Property("Id") diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260812194642_Initial.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260817083025_Initial.cs similarity index 99% rename from api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260812194642_Initial.cs rename to api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260817083025_Initial.cs index 8a9b76ea..2f524940 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260812194642_Initial.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/20260817083025_Initial.cs @@ -1035,6 +1035,29 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_Sas_H_BlogCategory", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Sas_H_ChangeLog", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Version = table.Column(type: "nvarchar(32)", maxLength: 32, nullable: false), + ReleaseDate = table.Column(type: "datetime2", nullable: false), + Commit = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), + Notes = table.Column(type: "nvarchar(max)", nullable: true), + IsPublished = table.Column(type: "bit", nullable: false), + CreationTime = table.Column(type: "datetime2", nullable: false), + CreatorId = table.Column(type: "uniqueidentifier", nullable: true), + LastModificationTime = table.Column(type: "datetime2", nullable: true), + LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), + IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), + DeleterId = table.Column(type: "uniqueidentifier", nullable: true), + DeletionTime = table.Column(type: "datetime2", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Sas_H_ChangeLog", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Sas_H_Contact", columns: table => new @@ -1700,29 +1723,6 @@ namespace Sozsoft.Platform.Migrations table.PrimaryKey("PK_Sas_H_Product", x => x.Id); }); - migrationBuilder.CreateTable( - name: "Sas_H_ReleaseNote", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Version = table.Column(type: "nvarchar(32)", maxLength: 32, nullable: false), - ReleaseDate = table.Column(type: "datetime2", nullable: false), - Commit = table.Column(type: "nvarchar(64)", maxLength: 64, nullable: true), - Notes = table.Column(type: "nvarchar(max)", nullable: true), - IsPublished = table.Column(type: "bit", nullable: false), - CreationTime = table.Column(type: "datetime2", nullable: false), - CreatorId = table.Column(type: "uniqueidentifier", nullable: true), - LastModificationTime = table.Column(type: "datetime2", nullable: true), - LastModifierId = table.Column(type: "uniqueidentifier", nullable: true), - IsDeleted = table.Column(type: "bit", nullable: false, defaultValue: false), - DeleterId = table.Column(type: "uniqueidentifier", nullable: true), - DeletionTime = table.Column(type: "datetime2", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Sas_H_ReleaseNote", x => x.Id); - }); - migrationBuilder.CreateTable( name: "Sas_H_Route", columns: table => new @@ -4057,6 +4057,13 @@ namespace Sozsoft.Platform.Migrations unique: true, filter: "[IsDeleted] = 0"); + migrationBuilder.CreateIndex( + name: "IX_Sas_H_ChangeLog_Version", + table: "Sas_H_ChangeLog", + column: "Version", + unique: true, + filter: "[IsDeleted] = 0"); + migrationBuilder.CreateIndex( name: "IX_Sas_H_City_Country_Name", table: "Sas_H_City", @@ -4257,13 +4264,6 @@ namespace Sozsoft.Platform.Migrations unique: true, filter: "[IsDeleted] = 0"); - migrationBuilder.CreateIndex( - name: "IX_Sas_H_ReleaseNote_Version", - table: "Sas_H_ReleaseNote", - column: "Version", - unique: true, - filter: "[IsDeleted] = 0"); - migrationBuilder.CreateIndex( name: "IX_Sas_H_Route_Key", table: "Sas_H_Route", @@ -4526,6 +4526,9 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Sas_H_BlogPost"); + migrationBuilder.DropTable( + name: "Sas_H_ChangeLog"); + migrationBuilder.DropTable( name: "Sas_H_Contact"); @@ -4604,9 +4607,6 @@ namespace Sozsoft.Platform.Migrations migrationBuilder.DropTable( name: "Sas_H_Product"); - migrationBuilder.DropTable( - name: "Sas_H_ReleaseNote"); - migrationBuilder.DropTable( name: "Sas_H_Route"); diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs index 049191ad..55fc7215 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Migrations/PlatformDbContextModelSnapshot.cs @@ -1378,6 +1378,68 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_T_BranchUsers", (string)null); }); + modelBuilder.Entity("Sozsoft.Platform.Entities.ChangeLog", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Commit") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("CreationTime") + .HasColumnType("datetime2") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uniqueidentifier") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uniqueidentifier") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("datetime2") + .HasColumnName("DeletionTime"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsPublished") + .HasColumnType("bit"); + + b.Property("LastModificationTime") + .HasColumnType("datetime2") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uniqueidentifier") + .HasColumnName("LastModifierId"); + + b.Property("Notes") + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("nvarchar(32)"); + + b.HasKey("Id"); + + b.HasIndex("Version") + .IsUnique() + .HasFilter("[IsDeleted] = 0"); + + b.ToTable("Sas_H_ChangeLog", (string)null); + }); + modelBuilder.Entity("Sozsoft.Platform.Entities.City", b => { b.Property("Id") @@ -4659,68 +4721,6 @@ namespace Sozsoft.Platform.Migrations b.ToTable("Sas_H_Product", (string)null); }); - modelBuilder.Entity("Sozsoft.Platform.Entities.ReleaseNote", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Commit") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("CreationTime") - .HasColumnType("datetime2") - .HasColumnName("CreationTime"); - - b.Property("CreatorId") - .HasColumnType("uniqueidentifier") - .HasColumnName("CreatorId"); - - b.Property("DeleterId") - .HasColumnType("uniqueidentifier") - .HasColumnName("DeleterId"); - - b.Property("DeletionTime") - .HasColumnType("datetime2") - .HasColumnName("DeletionTime"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(false) - .HasColumnName("IsDeleted"); - - b.Property("IsPublished") - .HasColumnType("bit"); - - b.Property("LastModificationTime") - .HasColumnType("datetime2") - .HasColumnName("LastModificationTime"); - - b.Property("LastModifierId") - .HasColumnType("uniqueidentifier") - .HasColumnName("LastModifierId"); - - b.Property("Notes") - .HasColumnType("nvarchar(max)"); - - b.Property("ReleaseDate") - .HasColumnType("datetime2"); - - b.Property("Version") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("nvarchar(32)"); - - b.HasKey("Id"); - - b.HasIndex("Version") - .IsUnique() - .HasFilter("[IsDeleted] = 0"); - - b.ToTable("Sas_H_ReleaseNote", (string)null); - }); - modelBuilder.Entity("Sozsoft.Platform.Entities.ReportCategory", b => { b.Property("Id") diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/ReleaseNoteStartupService.cs b/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/ChangeLogStartupService.cs similarity index 87% rename from api/src/Sozsoft.Platform.HttpApi.Host/Extensions/ReleaseNoteStartupService.cs rename to api/src/Sozsoft.Platform.HttpApi.Host/Extensions/ChangeLogStartupService.cs index 38416248..f16fb723 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/ReleaseNoteStartupService.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/ChangeLogStartupService.cs @@ -17,7 +17,7 @@ using Volo.Abp.Uow; namespace Sozsoft.Platform.Extensions; -public class ReleaseNoteSeedDto +public class ChangeLogSeedDto { public string Version { get; set; } public string ReleaseDate { get; set; } @@ -25,16 +25,16 @@ public class ReleaseNoteSeedDto public List Notes { get; set; } } -public class ReleaseNoteSeedFileDto +public class ChangeLogSeedFileDto { - public List ReleaseNotes { get; set; } + public List ChangeLogs { get; set; } } /// /// Sürüm notlarını uygulama açılışında veritabanına senkronize eder. /// -/// Kaynak, konteynere mount edilen release-notes.json dosyasıdır; bu dosya -/// deploy sırasında configs/release-notes/generate-release-notes.sh tarafından +/// Kaynak, konteynere mount edilen change-logs.json dosyasıdır; bu dosya +/// deploy sırasında configs/change-logs/generate-change-logs.sh tarafından /// git tag'lerinden üretilir. Böylece yeni bir sürüm tag'i eklemek için DbMigrator /// çalıştırmak GEREKMEZ — API'yi deploy etmek yeterlidir. /// @@ -45,25 +45,25 @@ public class ReleaseNoteSeedFileDto /// tag güncellenir, silinen tag veritabanından da kaldırılır. Dosya okunamaz veya /// boşsa hiçbir şey yapılmaz (yanlışlıkla tablo boşaltılmasın diye). /// -public class ReleaseNoteStartupService : IHostedService +public class ChangeLogStartupService : IHostedService { - private const string FileName = "release-notes.json"; + private const string FileName = "change-logs.json"; // Konteynerde docker-compose-app.yml ile mount edilen yol. - private const string MountedDirectory = "/etc/api/release-notes"; + private const string MountedDirectory = "/etc/api/change-logs"; private readonly IServiceProvider _serviceProvider; private readonly IHostApplicationLifetime _applicationLifetime; private readonly IHostEnvironment _environment; private readonly IConfiguration _configuration; - private readonly ILogger _logger; + private readonly ILogger _logger; - public ReleaseNoteStartupService( + public ChangeLogStartupService( IServiceProvider serviceProvider, IHostApplicationLifetime applicationLifetime, IHostEnvironment environment, IConfiguration configuration, - ILogger logger) + ILogger logger) { _serviceProvider = serviceProvider; _applicationLifetime = applicationLifetime; @@ -105,11 +105,11 @@ public class ReleaseNoteStartupService : IHostedService try { await using var stream = File.OpenRead(path); - var data = await JsonSerializer.DeserializeAsync( + var data = await JsonSerializer.DeserializeAsync( stream, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - var items = data?.ReleaseNotes ?? []; + var items = data?.ChangeLogs ?? []; if (items.Count == 0) { return; @@ -145,13 +145,13 @@ public class ReleaseNoteStartupService : IHostedService } /// - /// Dosya yolunu sırayla dener: açık ayar (App:ReleaseNotesPath), konteynerdeki - /// mount noktası, son olarak yerel geliştirmede depodaki configs/release-notes + /// Dosya yolunu sırayla dener: açık ayar (App:ChangeLogsPath), konteynerdeki + /// mount noktası, son olarak yerel geliştirmede depodaki configs/change-logs /// klasörü. Hiçbiri yoksa null döner. /// private string ResolvePath() { - var configured = _configuration["App:ReleaseNotesPath"]; + var configured = _configuration["App:ChangeLogsPath"]; if (!string.IsNullOrWhiteSpace(configured)) { // Klasör verildiyse dosya adı eklenir. @@ -166,11 +166,11 @@ public class ReleaseNoteStartupService : IHostedService } // Yerel geliştirme: ContentRootPath api/src/Sozsoft.Platform.HttpApi.Host, - // aranan yol depo kökündeki configs/release-notes. + // aranan yol depo kökündeki configs/change-logs. var directory = new DirectoryInfo(_environment.ContentRootPath); while (directory != null) { - var candidate = Path.Combine(directory.FullName, "configs", "release-notes", FileName); + var candidate = Path.Combine(directory.FullName, "configs", "change-logs", FileName); if (File.Exists(candidate)) { return candidate; @@ -184,9 +184,9 @@ public class ReleaseNoteStartupService : IHostedService private static async Task<(int Inserted, int Updated, int Deleted)> SyncItemsAsync( IServiceProvider services, - List items) + List items) { - var repository = services.GetRequiredService>(); + var repository = services.GetRequiredService>(); var unitOfWorkManager = services.GetRequiredService(); var currentTenant = services.GetRequiredService(); @@ -209,7 +209,7 @@ public class ReleaseNoteStartupService : IHostedService if (!existing.TryGetValue(item.Version, out var current)) { - await repository.InsertAsync(new ReleaseNote(item.Version, releaseDate, notes, item.Commit)); + await repository.InsertAsync(new ChangeLog(item.Version, releaseDate, notes, item.Commit)); inserted++; continue; } diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs b/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs index 0c2c951a..4fa601a7 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Extensions/PlatformApplicationConfigurationContributor.cs @@ -27,12 +27,12 @@ public class PlatformApplicationConfigurationContributor : IApplicationConfigura // Uygulamanın çalışan sürümü: istemcinin tek sürüm kaynağı (ayrı bir // version.json isteği yoktur). Yalnızca EN SON sürüm taşınır; tam liste - // ChangeLog ekranının kendi endpoint'inden (/api/app/release-note) gelir, + // ChangeLog ekranının kendi endpoint'inden (/api/app/change-log) gelir, // aksi halde her config isteğinde bütün changelog gönderilirdi. - var releaseNoteRepository = context.ServiceProvider.GetRequiredService>(); - var releaseNotes = await releaseNoteRepository.GetListAsync(x => x.IsPublished); + var changeLogRepository = context.ServiceProvider.GetRequiredService>(); + var changeLogs = await changeLogRepository.GetListAsync(x => x.IsPublished); - var latest = releaseNotes + var latest = changeLogs .OrderByDescending(note => note.ReleaseDate) .ThenByDescending(note => note.Version) .FirstOrDefault(); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs b/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs index 1c3612fe..f7b7f2a1 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs +++ b/api/src/Sozsoft.Platform.HttpApi.Host/PlatformHttpApiHostModule.cs @@ -140,9 +140,9 @@ public class PlatformHttpApiHostModule : AbpModule ConfigureCache(); ConfigureHangfire(context, configuration); context.Services.AddHostedService(); - // Sürüm notları imaja gömülü release-notes.json'dan senkronize edilir; + // Sürüm notları imaja gömülü change-logs.json'dan senkronize edilir; // yeni bir sürüm tag'i için DbMigrator çalıştırmak gerekmez. - context.Services.AddHostedService(); + context.Services.AddHostedService(); ConfigureBlobStoring(configuration); ConfigureAuditing(); diff --git a/api/src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj b/api/src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj index aa16fac6..617dab1f 100644 --- a/api/src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj +++ b/api/src/Sozsoft.Platform.HttpApi.Host/Sozsoft.Platform.HttpApi.Host.csproj @@ -54,8 +54,8 @@ - diff --git a/configs/change-logs/README.md b/configs/change-logs/README.md new file mode 100644 index 00000000..69288cd3 --- /dev/null +++ b/configs/change-logs/README.md @@ -0,0 +1,18 @@ +# change-logs + +`change-logs.json` git tag'lerinden üretilir ve API konteynerine salt okunur +mount edilir (`/etc/api/change-logs`). İmaja gömülmez. + +- Üretim: `bash configs/change-logs/generate-change-logs.sh` +- Deploy akışında otomatik çağrılır: `configs/deployment/scripts/deploy/app.sh` +- Okuyan taraf: `ChangeLogStartupService` (uygulama açılışında `Sas_H_ChangeLog` + tablosunu bu dosyayla eşitler) + +Bu klasör API build context'inin dışındadır; böylece yeni bir tag atmak API imajını +yeniden derletmez, docker cache'i bozulmaz. + +Farklı bir yol kullanılacaksa `App:ChangeLogsPath` (env: `App__ChangeLogsPath`) +ayarı ile ezilebilir. + +`change-logs.json` `.gitignore`'dadır; deploy'daki `git pull` ile çakışmaması için +takip edilmez. diff --git a/configs/release-notes/generate-release-notes.sh b/configs/change-logs/generate-change-logs.sh similarity index 90% rename from configs/release-notes/generate-release-notes.sh rename to configs/change-logs/generate-change-logs.sh index 3f897353..91dd2164 100644 --- a/configs/release-notes/generate-release-notes.sh +++ b/configs/change-logs/generate-change-logs.sh @@ -2,11 +2,11 @@ set -e # Sürüm notlarını git tag'lerinden üretir: -# configs/release-notes/release-notes.json +# configs/change-logs/change-logs.json # # Dosya İMAJA GÖMÜLMEZ. Konteynere docker-compose-app.yml üzerinden -# `/etc/api/release-notes` altına salt okunur mount edilir; ReleaseNoteStartupService -# açılışta bu yolu okuyup `Sas_H_ReleaseNote` tablosuyla eşitler, DbMigrator gerekmez. +# `/etc/api/change-logs` altına salt okunur mount edilir; ChangeLogStartupService +# açılışta bu yolu okuyup `Sas_H_ChangeLog` tablosuyla eşitler, DbMigrator gerekmez. # # Neden api/ altında değil: dosya api build context'i içindeyken her yeni tag # Dockerfile'daki `COPY . .` katmanını geçersiz kılıyor ve kodda hiçbir değişiklik @@ -21,14 +21,14 @@ set -e # Dış bağımlılığı yoktur (jq/node gerekmez): JSON kaçışları sed ile yapılır. cd "$(dirname "$0")" -OUTPUT="release-notes.json" +OUTPUT="change-logs.json" if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then echo "> git deposu yok, ${OUTPUT} güncellenmedi." >&2 exit 0 fi -echo "> release-notes.json oluşturuluyor..." +echo "> change-logs.json oluşturuluyor..." # origin'de olmayan yerel tag'ler sürüm sırasını bozar; temizle. `--force` şart: # yerel bir tag origin'dekinden farklı commit'e bakıyorsa git onu "would clobber @@ -49,7 +49,7 @@ escape_json() { } TMP=$(mktemp) -printf '{\n "ReleaseNotes": [\n' > "$TMP" +printf '{\n "ChangeLogs": [\n' > "$TMP" FIRST_TAG=true for TAG in $TAGS; do diff --git a/configs/deployment/docker-compose-app.yml b/configs/deployment/docker-compose-app.yml index 5127e323..8688ff69 100644 --- a/configs/deployment/docker-compose-app.yml +++ b/configs/deployment/docker-compose-app.yml @@ -45,9 +45,9 @@ services: - ../seeds:/etc/api/seeds - api-keys:/root/.aspnet/DataProtection-Keys # Surum notlari imaja gomulmez; deploy sirasinda git tag'lerinden uretilip - # buradan okunur (ReleaseNoteStartupService). Boylece yeni tag API imajini + # buradan okunur (ChangeLogStartupService). Boylece yeni tag API imajini # yeniden derletmez. - - ../release-notes:/etc/api/release-notes:ro + - ../change-logs:/etc/api/change-logs:ro networks: - db - default diff --git a/configs/deployment/scripts/build/api.sh b/configs/deployment/scripts/build/api.sh index 62a56200..efd179a7 100644 --- a/configs/deployment/scripts/build/api.sh +++ b/configs/deployment/scripts/build/api.sh @@ -9,7 +9,7 @@ cd ~/sozsoft-platform/api # Sürüm notları burada ÜRETİLMEZ. Dosya api build context'i içindeyken her yeni # tag Dockerfile'daki `COPY . .` katmanını geçersiz kılıyor ve kodda hiçbir -# değişiklik olmasa bile imaj baştan derleniyordu. Artık configs/release-notes +# değişiklik olmasa bile imaj baştan derleniyordu. Artık configs/change-logs # altında deploy sırasında üretilip konteynere mount ediliyor # (configs/deployment/scripts/deploy/app.sh). diff --git a/configs/deployment/scripts/deploy/app.sh b/configs/deployment/scripts/deploy/app.sh index 779486ff..7f7a982c 100644 --- a/configs/deployment/scripts/deploy/app.sh +++ b/configs/deployment/scripts/deploy/app.sh @@ -14,14 +14,14 @@ git checkout main # farklı commit'e bakıyorsa --force olmadan reddedilir ve yeni tag'ler gelmez. git fetch --prune --prune-tags --tags --force && git pull -# Sürüm notları imaja gömülü değil; konteynere configs/release-notes klasöründen +# Sürüm notları imaja gömülü değil; konteynere configs/change-logs klasöründen # mount ediliyor ve API açılışta veritabanına senkronize ediyor (DbMigrator # gerekmez). `bash ...` ile çağrılır: depo Windows'tan yönetildiği için # çalıştırma biti kaybolunca "Permission denied" verip notları sessizce düşürüyordu. -RELEASE_NOTES=~/sozsoft-platform/configs/release-notes/release-notes.json -bash ~/sozsoft-platform/configs/release-notes/generate-release-notes.sh +RELEASE_NOTES=~/sozsoft-platform/configs/change-logs/change-logs.json +bash ~/sozsoft-platform/configs/change-logs/generate-change-logs.sh -# Dosya yoksa API açılışta "release-notes.json bulunamadı" uyarısı verir ve +# Dosya yoksa API açılışta "change-logs.json bulunamadı" uyarısı verir ve # changelog eskir; sessizce devam etmek yerine burada dur. if [ ! -s "$RELEASE_NOTES" ]; then echo "HATA: ${RELEASE_NOTES} üretilemedi, deploy durduruldu." >&2 diff --git a/configs/release-notes/README.md b/configs/release-notes/README.md deleted file mode 100644 index 6490629e..00000000 --- a/configs/release-notes/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# release-notes - -`release-notes.json` git tag'lerinden üretilir ve API konteynerine salt okunur -mount edilir (`/etc/api/release-notes`). İmaja gömülmez. - -- Üretim: `bash configs/release-notes/generate-release-notes.sh` -- Deploy akışında otomatik çağrılır: `configs/deployment/scripts/deploy/app.sh` -- Okuyan taraf: `ReleaseNoteStartupService` (uygulama açılışında `Sas_H_ReleaseNote` - tablosunu bu dosyayla eşitler) - -Bu klasör API build context'inin dışındadır; böylece yeni bir tag atmak API imajını -yeniden derletmez, docker cache'i bozulmaz. - -Farklı bir yol kullanılacaksa `App:ReleaseNotesPath` (env: `App__ReleaseNotesPath`) -ayarı ile ezilebilir. - -`release-notes.json` `.gitignore`'dadır; deploy'daki `git pull` ile çakışmaması için -takip edilmez. diff --git a/ui/src/services/releaseNote.service.ts b/ui/src/services/changeLog.service.ts similarity index 52% rename from ui/src/services/releaseNote.service.ts rename to ui/src/services/changeLog.service.ts index 16a006e7..1e5aa63b 100644 --- a/ui/src/services/releaseNote.service.ts +++ b/ui/src/services/changeLog.service.ts @@ -1,25 +1,25 @@ import apiService from '@/services/api.service' -/** `Sas_H_ReleaseNote` kaydı; git tag'lerinden tohumlanır. */ -export interface ReleaseNoteDto { +/** `Sas_H_ChangeLog` kaydı; git tag'lerinden tohumlanır. */ +export interface ChangeLogDto { id: string version: string releaseDate: string changeLog: string[] } -class ReleaseNoteService { +class ChangeLogService { /** * ChangeLog ekranının kaynağı. Uygulamanın çalışan sürüm numarası * application-configuration ile geldiği için burada yalnızca tam liste alınır. */ - async getList(): Promise { - const response = await apiService.fetchData({ - url: '/api/app/release-note', + async getList(): Promise { + const response = await apiService.fetchData({ + url: '/api/app/change-log', method: 'GET', }) return response.data } } -export const releaseNoteService = new ReleaseNoteService() +export const changeLogService = new ChangeLogService() diff --git a/ui/src/views/version/ChangeLog.tsx b/ui/src/views/version/ChangeLog.tsx index cc998900..f7946461 100644 --- a/ui/src/views/version/ChangeLog.tsx +++ b/ui/src/views/version/ChangeLog.tsx @@ -5,7 +5,7 @@ import { useLocalization } from '@/utils/hooks/useLocalization' import React, { useEffect, useState } from 'react' import PageTitle from '@/components/shared/PageTitle' import { FaRegCheckCircle, FaSyncAlt, FaTag } from 'react-icons/fa' -import { releaseNoteService, type ReleaseNoteDto } from '@/services/releaseNote.service' +import { changeLogService, type ChangeLogDto } from '@/services/changeLog.service' import { checkForAppUpdate } from './swRegistration' import { useStoreActions } from '@/store' @@ -96,16 +96,16 @@ const Changelog = () => { const { isCheckingUpdate, checkForUpdate } = useManualUpdateCheck() const clearPendingChangelog = useStoreActions((actions) => actions.locale.clearPendingChangelog) - // Kaynak `ReleaseNote` entity'sidir; liste yalnızca bu ekran açıldığında + // Kaynak `ChangeLog` entity'sidir; liste yalnızca bu ekran açıldığında // istenir (application-configuration sadece çalışan sürümü taşır). - const [releases, setReleases] = useState([]) + const [releases, setReleases] = useState([]) useEffect(() => { // Bekleyen changelog bayrağı burada tüketilir; kullanıcı değişiklikleri // gördüğü için sonraki girişlerde tekrar yönlendirilmez. clearPendingChangelog() - void releaseNoteService + void changeLogService .getList() .then(setReleases) .catch((error: unknown) => console.warn('Sürüm notları alınamadı.', error))