From d280e0a056d76b84b71891ebd2775349ef98a3ad Mon Sep 17 00:00:00 2001 From: Carlos Alexandro Becker Date: Thu, 5 Sep 2024 13:28:00 -0300 Subject: [PATCH] feat(rpm): support %config(missingok) closes #853 --- apk/apk_test.go | 5 +++++ arch/arch.go | 2 +- deb/deb.go | 2 +- deb/deb_test.go | 5 +++++ files/files.go | 11 ++++++++--- ipk/ipk.go | 4 ++-- ipk/ipk_test.go | 5 +++++ rpm/rpm.go | 2 ++ rpm/rpm_test.go | 36 ++++++++++++++++++++++++++++++++++++ www/docs/configuration.md | 6 ++++++ 10 files changed, 71 insertions(+), 7 deletions(-) diff --git a/apk/apk_test.go b/apk/apk_test.go index a0ce0a2e..54961528 100644 --- a/apk/apk_test.go +++ b/apk/apk_test.go @@ -81,6 +81,11 @@ func exampleInfo() *nfpm.Info { Destination: "/etc/fake/fake2.conf", Type: files.TypeConfigNoReplace, }, + { + Source: "../testdata/whatever.conf", + Destination: "/etc/fake/fake3.conf", + Type: files.TypeConfigMissingOK, + }, { Destination: "/var/log/whatever", Type: files.TypeDir, diff --git a/arch/arch.go b/arch/arch.go index a35d021d..a5cb3faa 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -359,7 +359,7 @@ func createPkginfo(info *nfpm.Info, tw *tar.Writer, totalSize int64) (*MtreeEntr } for _, content := range info.Contents { - if content.Type == files.TypeConfig || content.Type == files.TypeConfigNoReplace { + if content.Type == files.TypeConfig || content.Type == files.TypeConfigNoReplace || content.Type == files.TypeConfigMissingOK { path := files.AsRelativePath(content.Destination) if err := writeKVPair(buf, "backup", path); err != nil { diff --git a/deb/deb.go b/deb/deb.go index e5218fe4..3e58bb89 100644 --- a/deb/deb.go +++ b/deb/deb.go @@ -681,7 +681,7 @@ func conffiles(info *nfpm.Info) []byte { var confs []string for _, file := range info.Contents { switch file.Type { - case files.TypeConfig, files.TypeConfigNoReplace: + case files.TypeConfig, files.TypeConfigNoReplace, files.TypeConfigMissingOK: confs = append(confs, files.NormalizeAbsoluteFilePath(file.Destination)) } } diff --git a/deb/deb_test.go b/deb/deb_test.go index 86e65a5c..30195cfe 100644 --- a/deb/deb_test.go +++ b/deb/deb_test.go @@ -84,6 +84,11 @@ func exampleInfo() *nfpm.Info { Destination: "/etc/fake/fake2.conf", Type: files.TypeConfigNoReplace, }, + { + Source: "../testdata/whatever.conf", + Destination: "/etc/fake/fake3.conf", + Type: files.TypeConfigMissingOK, + }, { Destination: "/var/log/whatever", Type: files.TypeDir, diff --git a/files/files.go b/files/files.go index 68fe6a30..6095227a 100644 --- a/files/files.go +++ b/files/files.go @@ -32,9 +32,14 @@ const ( // user of the package. TypeConfig = "config" // TypeConfigNoReplace is like TypeConfig with an added noreplace directive - // that is respected by RPM-based distributions. For all other packages it - // is handled exactly like TypeConfig. + // that is respected by RPM-based distributions. + // that is respected by RPM-based distributions. + // For all other package formats it is handled exactly like TypeConfig. TypeConfigNoReplace = "config|noreplace" + // TypeConfigMissingOK is like TypeConfig with an added missingok directive + // that is respected by RPM-based distributions. + // For all other package formats it is handled exactly like TypeConfig. + TypeConfigMissingOK = "config|missingok" // TypeGhost is the type of an RPM ghost file which is ignored by other packagers. TypeRPMGhost = "ghost" // TypeRPMDoc is the type of an RPM doc file which is ignored by other packagers. @@ -289,7 +294,7 @@ func PrepareForPackager( if err != nil { return nil, fmt.Errorf("add tree: %w", err) } - case TypeConfig, TypeConfigNoReplace, TypeFile, "": + case TypeConfig, TypeConfigNoReplace, TypeConfigMissingOK, TypeFile, "": globbed, err := glob.Glob( filepath.ToSlash(content.Source), filepath.ToSlash(content.Destination), diff --git a/ipk/ipk.go b/ipk/ipk.go index 14be0ab9..5b588c0b 100644 --- a/ipk/ipk.go +++ b/ipk/ipk.go @@ -205,7 +205,7 @@ func populateDataTar(info *nfpm.Info, tw *tar.Writer) (instSize int64, err error ModTime: modtime.Get(info.MTime), Linkname: file.Source, }) - case files.TypeFile, files.TypeTree, files.TypeConfig, files.TypeConfigNoReplace: + case files.TypeFile, files.TypeTree, files.TypeConfig, files.TypeConfigNoReplace, files.TypeConfigMissingOK: size, err = writeFile(tw, file) default: // ignore everything else @@ -293,7 +293,7 @@ func conffiles(info *nfpm.Info) []byte { var confs []string for _, file := range info.Contents { switch file.Type { - case files.TypeConfig, files.TypeConfigNoReplace: + case files.TypeConfig, files.TypeConfigNoReplace, files.TypeConfigMissingOK: confs = append(confs, files.NormalizeAbsoluteFilePath(file.Destination)) } } diff --git a/ipk/ipk_test.go b/ipk/ipk_test.go index 67402c54..f56262d6 100644 --- a/ipk/ipk_test.go +++ b/ipk/ipk_test.go @@ -71,6 +71,11 @@ func exampleInfo() *nfpm.Info { Destination: "/etc/fake/fake2.conf", Type: files.TypeConfigNoReplace, }, + { + Source: "../testdata/whatever.conf", + Destination: "/etc/fake/fake3.conf", + Type: files.TypeConfigNoReplace, + }, { Destination: "/var/log/whatever", Type: files.TypeDir, diff --git a/rpm/rpm.go b/rpm/rpm.go index 3e98b709..fed99138 100644 --- a/rpm/rpm.go +++ b/rpm/rpm.go @@ -373,6 +373,8 @@ func createFilesInsideRPM(info *nfpm.Info, rpm *rpmpack.RPM) (err error) { file, err = asRPMFile(content, rpmpack.ConfigFile) case files.TypeConfigNoReplace: file, err = asRPMFile(content, rpmpack.ConfigFile|rpmpack.NoReplaceFile) + case files.TypeConfigMissingOK: + file, err = asRPMFile(content, rpmpack.ConfigFile|rpmpack.MissingOkFile) case files.TypeRPMGhost: if content.FileInfo.Mode == 0 { content.FileInfo.Mode = os.FileMode(0o644) diff --git a/rpm/rpm_test.go b/rpm/rpm_test.go index 7569871f..d9a95f07 100644 --- a/rpm/rpm_test.go +++ b/rpm/rpm_test.go @@ -595,6 +595,42 @@ func TestArches(t *testing.T) { }) } +func TestConfigMissingOK(t *testing.T) { + var ( + buildConfigFile = "../testdata/whatever.conf" + packageConfigFile = "/etc/fake/fake.conf" + ) + + info := &nfpm.Info{ + Name: "symlink-in-files", + Arch: "amd64", + Description: "This package's config references a file via symlink.", + Version: "1.0.0", + Maintainer: "maintainer", + Overridables: nfpm.Overridables{ + Contents: []*files.Content{ + { + Source: buildConfigFile, + Destination: packageConfigFile, + Type: files.TypeConfigMissingOK, + }, + }, + }, + } + + var rpmFileBuffer bytes.Buffer + err := Default.Package(info, &rpmFileBuffer) + require.NoError(t, err) + + expectedConfigContent, err := os.ReadFile(buildConfigFile) + require.NoError(t, err) + + packageConfigContent, err := extractFileFromRpm(rpmFileBuffer.Bytes(), packageConfigFile) + require.NoError(t, err) + + require.Equal(t, expectedConfigContent, packageConfigContent) +} + func TestConfigNoReplace(t *testing.T) { var ( buildConfigFile = "../testdata/whatever.conf" diff --git a/www/docs/configuration.md b/www/docs/configuration.md index 3d0a9e9c..10bbc3c2 100644 --- a/www/docs/configuration.md +++ b/www/docs/configuration.md @@ -209,6 +209,12 @@ contents: dst: /etc/bar.conf type: config|noreplace + # Corresponds to `%config(missingok)` if the packager is rpm, otherwise it + # is just a config file + - src: path/to/local/bar.conf + dst: /etc/bar.conf + type: config|missingok + # These files are not actually present in the package, but the file names # are added to the package header. From the RPM directives documentation: #