using sh.actions.package_cleanup.Models; using sh.actions.package_cleanup.Service; namespace sh.actions.package_cleanup; public class Worker( ILogger logger, IConfiguration configuration, IHostApplicationLifetime appLifetime, IPackageService packageService, IGiteaPackageService giteaPackageService ) : BackgroundService { private async Task DeletePackages(List packages, CancellationToken cancellationToken = default) { var dryRun = configuration["DRY_RUN"]?.ToLower() == "true"; if (dryRun) { logger.LogInformation("Dry run enabled, not deleting {Count} packages", packages.Count); return 0; } var deletedCount = 0; foreach (var giteaPackage in packages) { try { await giteaPackageService.DeletePackage(giteaPackage, cancellationToken); deletedCount++; } catch (Exception ex) { logger.LogError(ex, "Failed to delete package {PackageName} version {Version}", giteaPackage.Name, giteaPackage.Version); } } return deletedCount; } private void WriteGitHubOutput(string name, string value) { var githubOutput = configuration["GITHUB_OUTPUT"]; if (string.IsNullOrEmpty(githubOutput)) { logger.LogDebug("GITHUB_OUTPUT not set, skipping output: {Name}={Value}", name, value); return; } try { File.AppendAllText(githubOutput, $"{name}={value}{Environment.NewLine}"); logger.LogInformation("Wrote to GITHUB_OUTPUT: {Name}={Value}", name, value); } catch (Exception ex) { logger.LogError(ex, "Failed to write to GITHUB_OUTPUT file"); } } protected override async Task ExecuteAsync(CancellationToken cancellationToken) { try { // Parse comma-separated names var names = (configuration["NAMES"] ?? "") .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); if (names.Length == 0) { logger.LogWarning("No package names configured"); WriteGitHubOutput("deleted_packages", "0"); WriteGitHubOutput("processed_names", "0"); appLifetime.StopApplication(); return; } var totalDeleted = 0; // Process each name separately: collect -> filter -> delete foreach (var name in names) { logger.LogInformation("Processing packages for name '{Name}'", name); var packages = (await giteaPackageService.GetPackagesByNameAsync(name, cancellationToken)).ToList(); logger.LogInformation("Found {Count} packages for name '{Name}'", packages.Count, name); var packagesToDelete = packageService.FilterPackagesToDelete(packages); logger.LogInformation("Found {Count} packages to delete for name '{Name}'", packagesToDelete.Count, name); var deletedCount = await DeletePackages(packagesToDelete, cancellationToken); totalDeleted += deletedCount; logger.LogInformation("Deleted {Count} packages for name '{Name}'", deletedCount, name); logger.LogInformation("Cleanup finished for name '{Name}'", name); } logger.LogInformation("All package names processed successfully"); // Write outputs to GITHUB_OUTPUT WriteGitHubOutput("deleted_packages", totalDeleted.ToString()); WriteGitHubOutput("processed_names", names.Length.ToString()); } catch (Exception ex) { logger.LogError(ex, "Cleanup failed with an error"); WriteGitHubOutput("deleted_packages", "0"); WriteGitHubOutput("processed_names", "0"); } finally { appLifetime.StopApplication(); } } }