diff --git a/nfpm.go b/nfpm.go index 3dc3557..05bbb43 100644 --- a/nfpm.go +++ b/nfpm.go @@ -182,6 +182,7 @@ type RPM struct { // https://www.cl.cam.ac.uk/~jw35/docs/rpm_config.html ConfigNoReplaceFiles map[string]string `yaml:"config_noreplace_files,omitempty"` Signature RPMSignature `yaml:"signature,omitempty"` + GhostFiles []string `yaml:"ghost_files,omitempty"` } type RPMSignature struct { diff --git a/rpm/rpm.go b/rpm/rpm.go index a56d8f4..5438efe 100644 --- a/rpm/rpm.go +++ b/rpm/rpm.go @@ -350,6 +350,19 @@ func createFilesInsideRPM(info *nfpm.Info, rpm *rpmpack.RPM) error { } } + // note: the ghost files will be created as empty files when the package is installed, which is not + // correct: https://github.com/google/rpmpack/issues/51 + for _, destName := range info.RPM.GhostFiles { + rpm.AddFile(rpmpack.RPMFile{ + Name: destName, + Mode: 0644, + MTime: uint32(time.Now().UTC().Unix()), + Owner: "root", + Group: "root", + Type: rpmpack.GhostFile, + }) + } + return nil } diff --git a/rpm/rpm_test.go b/rpm/rpm_test.go index 8547456..b65dc52 100644 --- a/rpm/rpm_test.go +++ b/rpm/rpm_test.go @@ -615,6 +615,34 @@ func TestRPMSignatureError(t *testing.T) { require.True(t, errors.As(err, &expectedError)) } +func TestRPMGhostFiles(t *testing.T) { + var ( + filename = "/usr/lib/casper.a" + ) + + info := &nfpm.Info{ + Name: "rpm-ghost", + Arch: "amd64", + Description: "This RPM contains ghost files.", + Version: "1.0.0", + Overridables: nfpm.Overridables{RPM: nfpm.RPM{GhostFiles: []string{filename}}}, + } + + var rpmFileBuffer bytes.Buffer + err := Default.Package(info, &rpmFileBuffer) + require.NoError(t, err) + + packagedFileHeader, err := extractFileHeaderFromRpm(rpmFileBuffer.Bytes(), filename) + require.NoError(t, err) + + packagedFile, err := extractFileFromRpm(rpmFileBuffer.Bytes(), filename) + require.NoError(t, err) + + assert.Equal(t, filename, packagedFileHeader.Filename()) + assert.Equal(t, cpio.S_ISREG|0644, packagedFileHeader.Mode()) + assert.Equal(t, "", string(packagedFile)) +} + func extractFileFromRpm(rpm []byte, filename string) ([]byte, error) { rpmFile, err := rpmutils.ReadRpm(bytes.NewReader(rpm)) if err != nil { diff --git a/www/docs/configuration.md b/www/docs/configuration.md index 3590656..69dcaa1 100644 --- a/www/docs/configuration.md +++ b/www/docs/configuration.md @@ -131,7 +131,7 @@ overrides: apk: # ... -# Custon configuration applied only to the RPM packager. +# Custom configuration applied only to the RPM packager. rpm: # The package group. This option is deprecated by most distros # but required by old distros like CentOS 5 / EL 5 and earlier. @@ -149,6 +149,20 @@ rpm: config_noreplace_files: path/to/local/bar.con: /etc/bar.conf + # These files are not actually present in the package, but the file names + # are added to the package header. From the RPM directives documentation: + # + # "There are times when a file should be owned by the package but not + # installed - log files and state files are good examples of cases you might + # desire this to happen." + # + # "The way to achieve this, is to use the %ghost directive. By adding this + # directive to the line containing a file, RPM will know about the ghosted + # file, but will not add it to the package." + ghost_files: + - /etc/casper.conf + - /var/log/boo.log + # The package is signed if a key_file is set signature: # PGP secret key (can also be ASCII-armored), the passphrase is taken