From 00fb6d26056866d3ad3243262035cbda5db2980f Mon Sep 17 00:00:00 2001 From: nab0y Date: Mon, 24 Mar 2025 23:40:56 +0300 Subject: [PATCH 1/2] Add optional 'Group' to rpm .spec --- PupNet/Builders/RpmBuilder.cs | 5 +++++ PupNet/ConfigurationReader.cs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/PupNet/Builders/RpmBuilder.cs b/PupNet/Builders/RpmBuilder.cs index ec837d4..c3984c4 100644 --- a/PupNet/Builders/RpmBuilder.cs +++ b/PupNet/Builders/RpmBuilder.cs @@ -272,6 +272,11 @@ private string GetSpec() sb.AppendLine($"License: {Configuration.AppLicenseId}"); sb.AppendLine($"Vendor: {Configuration.PublisherName}"); + if (!string.IsNullOrEmpty(Configuration.RpmGroup)) + { + sb.AppendLine($"Group: {Configuration.RpmGroup}"); + } + if (!string.IsNullOrEmpty(Configuration.PublisherLinkUrl)) { sb.AppendLine($"Url: {Configuration.PublisherLinkUrl}"); diff --git a/PupNet/ConfigurationReader.cs b/PupNet/ConfigurationReader.cs index e053b23..fde5c73 100644 --- a/PupNet/ConfigurationReader.cs +++ b/PupNet/ConfigurationReader.cs @@ -167,6 +167,7 @@ private ConfigurationReader(ArgumentReader args, IniReader reader, bool assertPa RpmAutoReq = GetBool(nameof(RpmAutoReq), RpmAutoReq); RpmAutoProv = GetBool(nameof(RpmAutoProv), RpmAutoProv); + RpmGroup = GetOptional(nameof(RpmGroup), ValueFlags.None); RpmRequires = GetCollection(nameof(RpmRequires), RpmRequires, ValueFlags.SafeNoSpace); DebianRecommends = GetCollection(nameof(DebianRecommends), DebianRecommends, ValueFlags.SafeNoSpace); @@ -270,6 +271,7 @@ private enum ValueFlags public bool RpmAutoReq { get; } = false; public bool RpmAutoProv { get; } = true; + public string? RpmGroup { get; } public IReadOnlyCollection RpmRequires { get; } = new string[] { "krb5-libs", "libicu", "openssl-libs", "zlib" }; @@ -582,6 +584,10 @@ public string ToString(DocStyles style) $"Boolean (true or false) which specifies whether to build the RPM package with 'AutoProv' equal to yes or no.", $"Refer: https://rpm-software-management.github.io/rpm/manual/spec.html")); + sb.Append(CreateHelpField(nameof(RpmGroup), RpmGroup, style, + $"The specified group must be in the list of groups known to RPM. This list is located in the file /usr/lib/rpm/GROUPS", + $"which is part of the rpm package.")); + sb.Append(CreateHelpField(nameof(RpmRequires), RpmRequires, true, style, $"Optional list of RPM dependencies. The list may include multiple values separated with semicolon or given", $"in multi-line form. If empty, a self-contained dotnet package will successfully run on many (but not all)", From 38dc171876b1f1f2c0748b01962e4c17ee2358df Mon Sep 17 00:00:00 2001 From: nab0y Date: Fri, 28 Mar 2025 21:54:02 +0300 Subject: [PATCH 2/2] Add pre/post install and pre/post install scripts to deb and rpm builders. --- PupNet/Builders/AppImageBuilder.cs | 5 ++ PupNet/Builders/DebianBuilder.cs | 75 ++++++++++++++++++++++++++++++ PupNet/Builders/FlatpakBuilder.cs | 5 ++ PupNet/Builders/RpmBuilder.cs | 49 +++++++++++++++++++ PupNet/Builders/SetupBuilder.cs | 5 ++ PupNet/Builders/ZipBuilder.cs | 5 ++ PupNet/ConfigurationReader.cs | 47 +++++++++++++++++-- PupNet/PackageBuilder.cs | 7 +++ 8 files changed, 194 insertions(+), 4 deletions(-) diff --git a/PupNet/Builders/AppImageBuilder.cs b/PupNet/Builders/AppImageBuilder.cs index 71b8a58..18d1f9a 100644 --- a/PupNet/Builders/AppImageBuilder.cs +++ b/PupNet/Builders/AppImageBuilder.cs @@ -157,6 +157,11 @@ public override string? MetaBuildPath /// public override string? ManifestBuildPath { get; } + public override IEnumerable<(string Path, string Content)> GetExtraContents() + { + yield break; + } + /// /// Implements. /// diff --git a/PupNet/Builders/DebianBuilder.cs b/PupNet/Builders/DebianBuilder.cs index 34772da..d297cd7 100644 --- a/PupNet/Builders/DebianBuilder.cs +++ b/PupNet/Builders/DebianBuilder.cs @@ -53,6 +53,34 @@ public DebianBuilder(ConfigurationReader conf) var archiveDirectory = Path.Combine(OutputDirectory, OutputName); cmd += $"--build \"{BuildRoot}\" \"{archiveDirectory}\""; list.Add(cmd); + + var extraPaths = new List(); + if (Configuration.DebianPreInst.Count != 0) + { + extraPaths.Add(Path.Combine(BuildRoot, "DEBIAN/preinst")); + } + + if (Configuration.DebianPostInst.Count != 0) + { + extraPaths.Add(Path.Combine(BuildRoot, "DEBIAN/postinst")); + } + + if (Configuration.DebianPreRm.Count != 0) + { + extraPaths.Add(Path.Combine(BuildRoot, "DEBIAN/prerm")); + } + + if (Configuration.DebianPostRm.Count != 0) + { + extraPaths.Add(Path.Combine(BuildRoot, "DEBIAN/postrm")); + } + + if (extraPaths.Count > 0) + { + // set permission must be set before build command + list.Insert(0, $"chmod +x {string.Join(' ', extraPaths)}"); + } + PackageCommands = list; } @@ -124,6 +152,53 @@ public override string Architecture /// public override string? ManifestBuildPath { get; } + public override IEnumerable<(string Path, string Content)> GetExtraContents() + { + if (Configuration.DebianPreInst.Count != 0) + { + var sb = new StringBuilder(); + foreach (var item in Configuration.DebianPreInst) + { + sb.AppendLine(item); + } + + yield return (Path.Combine(BuildRoot, "DEBIAN/preinst"), sb.ToString()); + } + + if (Configuration.DebianPostInst.Count != 0) + { + var sb = new StringBuilder(); + foreach (var item in Configuration.DebianPostInst) + { + sb.AppendLine(item); + } + + yield return (Path.Combine(BuildRoot, "DEBIAN/postinst"), sb.ToString()); + } + + if (Configuration.DebianPreRm.Count != 0) + { + var sb = new StringBuilder(); + foreach (var item in Configuration.DebianPreRm) + { + sb.AppendLine(item); + } + + yield return (Path.Combine(BuildRoot, "DEBIAN/prerm"), sb.ToString()); + } + + if (Configuration.DebianPostRm.Count != 0) + { + var sb = new StringBuilder(); + foreach (var item in Configuration.DebianPostRm) + { + sb.AppendLine(item); + } + + yield return (Path.Combine(BuildRoot, "DEBIAN/postrm"), sb.ToString()); + } + } + /// /// Implements. /// diff --git a/PupNet/Builders/FlatpakBuilder.cs b/PupNet/Builders/FlatpakBuilder.cs index d8e5f79..a206d67 100644 --- a/PupNet/Builders/FlatpakBuilder.cs +++ b/PupNet/Builders/FlatpakBuilder.cs @@ -120,6 +120,11 @@ public override string OutputName /// public override string? ManifestBuildPath { get; } + public override IEnumerable<(string Path, string Content)> GetExtraContents() + { + yield break; + } + /// /// Implements. /// diff --git a/PupNet/Builders/RpmBuilder.cs b/PupNet/Builders/RpmBuilder.cs index c3984c4..731aee8 100644 --- a/PupNet/Builders/RpmBuilder.cs +++ b/PupNet/Builders/RpmBuilder.cs @@ -141,6 +141,11 @@ public override string Architecture /// public override string? ManifestBuildPath { get; } + public override IEnumerable<(string Path, string Content)> GetExtraContents() + { + yield break; + } + /// /// Implements. /// @@ -343,6 +348,50 @@ private string GetSpec() } */ + if (Configuration.RpmPre.Count != 0) + { + sb.AppendLine(); + sb.AppendLine("%pre"); + + foreach (var item in Configuration.RpmPre) + { + sb.AppendLine(item); + } + } + + if (Configuration.RpmPost.Count != 0) + { + sb.AppendLine(); + sb.AppendLine("%post"); + + foreach (var item in Configuration.RpmPost) + { + sb.AppendLine(item); + } + } + + if (Configuration.RpmPreUn.Count != 0) + { + sb.AppendLine(); + sb.AppendLine("%preun"); + + foreach (var item in Configuration.RpmPreUn) + { + sb.AppendLine(item); + } + } + + if (Configuration.RpmPostUn.Count != 0) + { + sb.AppendLine(); + sb.AppendLine("%postun"); + + foreach (var item in Configuration.RpmPostUn) + { + sb.AppendLine(item); + } + } + // https://stackoverflow.com/questions/57385249/in-an-rpm-files-section-is-it-possible-to-specify-a-directory-and-all-of-its-fi sb.AppendLine(); sb.AppendLine("%files"); diff --git a/PupNet/Builders/SetupBuilder.cs b/PupNet/Builders/SetupBuilder.cs index 78ba05e..4adbe89 100644 --- a/PupNet/Builders/SetupBuilder.cs +++ b/PupNet/Builders/SetupBuilder.cs @@ -93,6 +93,11 @@ public override string Architecture /// public override string? ManifestBuildPath { get; } + public override IEnumerable<(string Path, string Content)> GetExtraContents() + { + yield break; + } + /// /// Implements. /// diff --git a/PupNet/Builders/ZipBuilder.cs b/PupNet/Builders/ZipBuilder.cs index b401c59..f43907b 100644 --- a/PupNet/Builders/ZipBuilder.cs +++ b/PupNet/Builders/ZipBuilder.cs @@ -79,6 +79,11 @@ public override string OutputName /// public override string? ManifestBuildPath { get; } + public override IEnumerable<(string Path, string Content)> GetExtraContents() + { + yield break; + } + /// /// Implements. /// diff --git a/PupNet/ConfigurationReader.cs b/PupNet/ConfigurationReader.cs index fde5c73..b7a55ef 100644 --- a/PupNet/ConfigurationReader.cs +++ b/PupNet/ConfigurationReader.cs @@ -168,9 +168,17 @@ private ConfigurationReader(ArgumentReader args, IniReader reader, bool assertPa RpmAutoReq = GetBool(nameof(RpmAutoReq), RpmAutoReq); RpmAutoProv = GetBool(nameof(RpmAutoProv), RpmAutoProv); RpmGroup = GetOptional(nameof(RpmGroup), ValueFlags.None); + RpmPre = GetCollection(nameof(RpmPre), ValueFlags.MultiText); + RpmPost = GetCollection(nameof(RpmPost), ValueFlags.MultiText); + RpmPreUn = GetCollection(nameof(RpmPreUn), ValueFlags.MultiText); + RpmPostUn = GetCollection(nameof(RpmPostUn), ValueFlags.MultiText); RpmRequires = GetCollection(nameof(RpmRequires), RpmRequires, ValueFlags.SafeNoSpace); DebianRecommends = GetCollection(nameof(DebianRecommends), DebianRecommends, ValueFlags.SafeNoSpace); + DebianPreInst = GetCollection(nameof(DebianPreInst), DebianPreInst, ValueFlags.MultiText); + DebianPostInst = GetCollection(nameof(DebianPostInst), DebianPostInst, ValueFlags.MultiText); + DebianPreRm = GetCollection(nameof(DebianPreRm), DebianPreRm, ValueFlags.MultiText); + DebianPostRm = GetCollection(nameof(DebianPostRm), DebianPostRm, ValueFlags.MultiText); FlatpakPlatformRuntime = GetMandatory(nameof(FlatpakPlatformRuntime), ValueFlags.StrictSafe); FlatpakPlatformSdk = GetMandatory(nameof(FlatpakPlatformSdk), ValueFlags.StrictSafe); @@ -272,11 +280,22 @@ private enum ValueFlags public bool RpmAutoReq { get; } = false; public bool RpmAutoProv { get; } = true; public string? RpmGroup { get; } + + public IReadOnlyCollection RpmPre { get; } = Array.Empty(); + public IReadOnlyCollection RpmPost { get; } = Array.Empty(); + public IReadOnlyCollection RpmPreUn { get; } = Array.Empty(); + public IReadOnlyCollection RpmPostUn { get; } = Array.Empty(); + public IReadOnlyCollection RpmRequires { get; } = new string[] { "krb5-libs", "libicu", "openssl-libs", "zlib" }; public IReadOnlyCollection DebianRecommends { get; } = new string[] { "libc6", "libgcc1", "libgcc-s1", "libgssapi-krb5-2", "libicu", "libssl", "libstdc++6", "libunwind", "zlib1g" }; + + public IReadOnlyCollection DebianPreInst { get; } = Array.Empty(); + public IReadOnlyCollection DebianPostInst { get; } = Array.Empty(); + public IReadOnlyCollection DebianPreRm { get; } = Array.Empty(); + public IReadOnlyCollection DebianPostRm { get; } = Array.Empty(); public string? SetupGroupName { get; } public bool SetupAdminInstall { get; } @@ -587,6 +606,18 @@ public string ToString(DocStyles style) sb.Append(CreateHelpField(nameof(RpmGroup), RpmGroup, style, $"The specified group must be in the list of groups known to RPM. This list is located in the file /usr/lib/rpm/GROUPS", $"which is part of the rpm package.")); + + sb.Append(CreateHelpField(nameof(RpmPre), RpmPre, true, style, + "The script is executed before the package is installed into the system.")); + + sb.Append(CreateHelpField(nameof(RpmPost), RpmPost, true, style, + "The script is executed after the package is installed into the system.")); + + sb.Append(CreateHelpField(nameof(RpmPreUn), RpmPreUn, true, style, + "The script is executed before the package is removed from the system.")); + + sb.Append(CreateHelpField(nameof(RpmPostUn), RpmPostUn, true, style, + "The script is executed after the package is removed from the system.")); sb.Append(CreateHelpField(nameof(RpmRequires), RpmRequires, true, style, $"Optional list of RPM dependencies. The list may include multiple values separated with semicolon or given", @@ -595,10 +626,8 @@ public string ToString(DocStyles style) $"Default values are recommended for use with dotnet and RPM packages at the time of writing.", $"For updated information, see: https://learn.microsoft.com/en-us/dotnet/core/install/linux-rhel#dependencies")); - - sb.Append(CreateBreaker("DEBIAN OPTIONS", style)); - + sb.Append(CreateHelpField(nameof(DebianRecommends), DebianRecommends, true, style, $"Optional list of Debian dependencies. The list may include multiple values separated with semicolon or given", $"in multi-line form. If empty, a self-contained dotnet package will successfully run on many (but not all)", @@ -606,7 +635,17 @@ public string ToString(DocStyles style) $"Default values are recommended for use with dotnet and Debian packages at the time of writing.", $"For updated information, see: https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu#dependencies")); - + sb.Append(CreateHelpField(nameof(DebianPreInst), DebianPreInst, true, style, + "The script is executed before the package is installed into the system.")); + + sb.Append(CreateHelpField(nameof(DebianPostInst), DebianPostInst, true, style, + "The script is executed after the package is installed into the system.")); + + sb.Append(CreateHelpField(nameof(DebianPreRm), DebianPreRm, true, style, + "The script is executed before the package is removed from the system.")); + + sb.Append(CreateHelpField(nameof(DebianPostRm), DebianPostRm, true, style, + "The script is executed after the package is removed from the system.")); sb.Append(CreateBreaker("WINDOWS SETUP OPTIONS", style)); diff --git a/PupNet/PackageBuilder.cs b/PupNet/PackageBuilder.cs index 666e034..f447cda 100644 --- a/PupNet/PackageBuilder.cs +++ b/PupNet/PackageBuilder.cs @@ -396,6 +396,8 @@ public string InstallExec /// public abstract string? ManifestBuildPath { get; } + public abstract IEnumerable<(string Path, string Content)> GetExtraContents(); + /// /// Gets the destination path of the LICENSE file in the build directory. This will cause /// to be copied into . @@ -579,6 +581,11 @@ public virtual void BuildPackage() // may change after Create() and dotnet publish in some deployments. Operations.WriteFile(ManifestBuildPath, ManifestContent); + foreach (var extraContent in GetExtraContents()) + { + Operations.WriteFile(extraContent.Path, extraContent.Content); + } + Operations.Execute(PackageCommands); }