feat: load application and repository configuration from env and TOML
This commit is contained in:
commit
ae8680d275
17 files changed
+301
No files matched your search
@@ -0,0 +1,25 @@
|
|||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/.idea
|
||||||
|
**/*.*proj.user
|
||||||
|
**/*.dbmdl
|
||||||
|
**/*.jfm
|
||||||
|
**/azds.yaml
|
||||||
|
**/bin
|
||||||
|
**/charts
|
||||||
|
**/docker-compose*
|
||||||
|
**/Dockerfile*
|
||||||
|
**/node_modules
|
||||||
|
**/npm-debug.log
|
||||||
|
**/obj
|
||||||
|
**/secrets.dev.yaml
|
||||||
|
**/values.dev.yaml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
||||||
|
|
||||||
|
.env
|
||||||
|
config.toml
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Rider ignored files
|
||||||
|
/modules.xml
|
||||||
|
/contentModel.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
/.idea.ForgejoReleaseSync.iml
|
||||||
|
# Ignored default folder with query files
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="TimeTrackingManager">
|
||||||
|
<option name="totallyTimeSpent" value="248000" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
FROM mcr.microsoft.com/dotnet/runtime:10.0 AS base
|
||||||
|
USER $APP_UID
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["ForgejoReleaseSync.csproj", "./"]
|
||||||
|
RUN dotnet restore "ForgejoReleaseSync.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/"
|
||||||
|
RUN dotnet build "./ForgejoReleaseSync.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./ForgejoReleaseSync.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "ForgejoReleaseSync.dll"]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<PublishAot>true</PublishAot>
|
||||||
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="DotNetEnv" Version="3.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.14" />
|
||||||
|
<PackageReference Include="RStein.TOML" Version="1.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ForgejoReleaseSync", "ForgejoReleaseSync.csproj", "{268E3A9E-52D0-4B81-A677-277555C6D1F8}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0E6F637D-3E7A-4DD3-8B01-1C7BFB36586A}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
compose.yaml = compose.yaml
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{268E3A9E-52D0-4B81-A677-277555C6D1F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{268E3A9E-52D0-4B81-A677-277555C6D1F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{268E3A9E-52D0-4B81-A677-277555C6D1F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{268E3A9E-52D0-4B81-A677-277555C6D1F8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
using ForgejoReleaseSync.Services;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
|
var config = AppConfigLoader.LoadConfigFromEnv();
|
||||||
|
builder.Services.AddSingleton(config);
|
||||||
|
|
||||||
|
var repositoryConfig = await RepoConfigLoader.LoadConfigAsync(
|
||||||
|
config.RepositoryConfigFilePath);
|
||||||
|
builder.Services.AddSingleton(repositoryConfig);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
await app.RunAsync();
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using DotNetEnv;
|
||||||
|
|
||||||
|
namespace ForgejoReleaseSync.Services;
|
||||||
|
|
||||||
|
public class AppConfig
|
||||||
|
{
|
||||||
|
public required string ForgejoInstacne { get; set; }
|
||||||
|
public required string ForgejoToken { get; set; }
|
||||||
|
public required ushort SyncConcurrency { get; set; }
|
||||||
|
public required string RepositoryConfigFilePath { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class AppConfigLoader
|
||||||
|
{
|
||||||
|
public static AppConfig LoadConfigFromEnv()
|
||||||
|
{
|
||||||
|
Env.Load();
|
||||||
|
|
||||||
|
var forgejoInstance = Environment.GetEnvironmentVariable("FORGEJO_INSTANCE");
|
||||||
|
var forgejoToken = Environment.GetEnvironmentVariable("FORGEJO_TOKEN");
|
||||||
|
var syncConcurrency = Environment.GetEnvironmentVariable("SYNC_CONCURRENCY");
|
||||||
|
var repoConfigFilePath = Environment.GetEnvironmentVariable("REPO_CONFIG_FILE");
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(forgejoInstance))
|
||||||
|
throw new Exception("FORGEJO_INSTANCE environment variable is required");
|
||||||
|
if (!string.IsNullOrEmpty(forgejoToken) && forgejoInstance.EndsWith('/'))
|
||||||
|
forgejoInstance = forgejoInstance[..^1];
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(forgejoToken))
|
||||||
|
throw new Exception("FORGEJO_TOKEN environment variable is required");
|
||||||
|
|
||||||
|
ushort syncConcurrencyParsed = 3;
|
||||||
|
if (!string.IsNullOrEmpty(syncConcurrency))
|
||||||
|
ushort.TryParse(syncConcurrency, out syncConcurrencyParsed);
|
||||||
|
if (syncConcurrencyParsed > 10)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(syncConcurrencyParsed),
|
||||||
|
"Sync concurrency cannot be greater than 10");
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(repoConfigFilePath))
|
||||||
|
throw new Exception("REPO_CONFIG_FILE environment variable is required");
|
||||||
|
|
||||||
|
|
||||||
|
return new AppConfig
|
||||||
|
{
|
||||||
|
ForgejoInstacne = forgejoInstance,
|
||||||
|
ForgejoToken = forgejoToken,
|
||||||
|
SyncConcurrency = syncConcurrencyParsed,
|
||||||
|
RepositoryConfigFilePath = repoConfigFilePath
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ForgejoReleaseSync.Services;
|
||||||
|
|
||||||
|
public class ForgejoService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace ForgejoReleaseSync.Services;
|
||||||
|
|
||||||
|
public class GithubService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using RStein.TOML;
|
||||||
|
|
||||||
|
namespace ForgejoReleaseSync.Services;
|
||||||
|
|
||||||
|
public class RepositoryConfig
|
||||||
|
{
|
||||||
|
public List<RepoSyncConfig> Repositories { get; set; } = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RepoSyncConfig
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required string GithubUrl { get; set; }
|
||||||
|
public required string ForgejoUrl { get; set; }
|
||||||
|
public required string IncludeRegex { get; set; }
|
||||||
|
public required string ExcludeRegex { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class RepoConfigLoader()
|
||||||
|
{
|
||||||
|
public static async Task<RepositoryConfig> LoadConfigAsync(string configPath,CancellationToken token = default)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var toml = await File.ReadAllTextAsync(configPath, token);
|
||||||
|
|
||||||
|
var tables = await TomlSerializer.DeserializeAsync(toml, token);
|
||||||
|
|
||||||
|
var result = new RepositoryConfig();
|
||||||
|
|
||||||
|
foreach (var table in tables)
|
||||||
|
{
|
||||||
|
if (table.Value is not TomlArray tableValues)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var content in tableValues)
|
||||||
|
{
|
||||||
|
if (content is not TomlTable fields)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
result.Repositories.Add(new RepoSyncConfig
|
||||||
|
{
|
||||||
|
Name = table.Key.ToString(),
|
||||||
|
GithubUrl = GetRequiredString(fields, "GithubUrl"),
|
||||||
|
ForgejoUrl = GetRequiredString(fields, "ForgejoUrl"),
|
||||||
|
IncludeRegex = GetRequiredString(fields, "IncludeRegex"),
|
||||||
|
ExcludeRegex = GetRequiredString(fields, "ExcludeRegex")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException("Failed to load configuration file", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetRequiredString(
|
||||||
|
TomlTable table,
|
||||||
|
string key)
|
||||||
|
{
|
||||||
|
return (string?)table[key]
|
||||||
|
?? throw new InvalidDataException(
|
||||||
|
$"Missing required configuration property: {key}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
forgejoreleasesync:
|
||||||
|
image: forgejoreleasesync
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[[repo1]]
|
||||||
|
GithubUrl = "https://github.com/owner/repo1"
|
||||||
|
ForgejoUrl = "https://forgejo.example.com/owner/repo1"
|
||||||
|
IncludeRegex = ".*"
|
||||||
|
ExcludeRegex = "^$"
|
||||||
|
|
||||||
|
[[repo2]]
|
||||||
|
GithubUrl = "https://github.com/owner/repo2"
|
||||||
|
ForgejoUrl = "https://forgejo.example.com/owner/repo2"
|
||||||
|
IncludeRegex = "^v.*"
|
||||||
|
ExcludeRegex = ".*-beta.*"
|
||||||
Reference in new issue
Block a user