github.com/goreleaser/nfpm/v2@v2.44.0/rpm/rpm_test.go (about)

     1  package rpm
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/ProtonMail/go-crypto/openpgp"
    15  	"github.com/goreleaser/chglog"
    16  	"github.com/goreleaser/nfpm/v2"
    17  	"github.com/goreleaser/nfpm/v2/files"
    18  	"github.com/goreleaser/nfpm/v2/internal/sign"
    19  	"github.com/sassoftware/go-rpmutils"
    20  	"github.com/sassoftware/go-rpmutils/cpio"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func exampleInfo() *nfpm.Info {
    25  	return setDefaults(nfpm.WithDefaults(&nfpm.Info{
    26  		Name:        "foo",
    27  		Arch:        "amd64",
    28  		Description: "Foo does things",
    29  		Priority:    "extra",
    30  		Maintainer:  "Carlos A Becker <pkg@carlosbecker.com>",
    31  		Version:     "1.0.0",
    32  		Release:     "1",
    33  		Epoch:       "0",
    34  		Section:     "default",
    35  		Homepage:    "http://carlosbecker.com",
    36  		Vendor:      "nope",
    37  		License:     "MIT",
    38  		Overridables: nfpm.Overridables{
    39  			Depends: []string{
    40  				"bash",
    41  			},
    42  			Recommends: []string{
    43  				"git",
    44  			},
    45  			Suggests: []string{
    46  				"bash",
    47  			},
    48  			Replaces: []string{
    49  				"svn",
    50  			},
    51  			Provides: []string{
    52  				"bzr",
    53  			},
    54  			Conflicts: []string{
    55  				"zsh",
    56  			},
    57  			Contents: []*files.Content{
    58  				{
    59  					Source:      "../testdata/fake",
    60  					Destination: "/usr/bin/fake",
    61  				},
    62  				{
    63  					Source:      "../testdata/whatever.conf",
    64  					Destination: "/etc/fake/fake.conf",
    65  					Type:        files.TypeConfig,
    66  				},
    67  				{
    68  					Destination: "/var/log/whatever",
    69  					Type:        files.TypeDir,
    70  				},
    71  				{
    72  					Destination: "/usr/share/whatever",
    73  					Type:        files.TypeDir,
    74  				},
    75  			},
    76  			Scripts: nfpm.Scripts{
    77  				PreInstall:  "../testdata/scripts/preinstall.sh",
    78  				PostInstall: "../testdata/scripts/postinstall.sh",
    79  				PreRemove:   "../testdata/scripts/preremove.sh",
    80  				PostRemove:  "../testdata/scripts/postremove.sh",
    81  			},
    82  			RPM: nfpm.RPM{
    83  				Group:     "foo",
    84  				BuildHost: "barhost",
    85  				Prefixes:  []string{"/opt"},
    86  				Scripts: nfpm.RPMScripts{
    87  					PreTrans:  "../testdata/scripts/pretrans.sh",
    88  					PostTrans: "../testdata/scripts/posttrans.sh",
    89  					Verify:    "../testdata/scripts/verify.sh",
    90  				},
    91  			},
    92  		},
    93  	}))
    94  }
    95  
    96  func TestConventionalExtension(t *testing.T) {
    97  	require.Equal(t, ".rpm", DefaultRPM.ConventionalExtension())
    98  }
    99  
   100  func TestRPM(t *testing.T) {
   101  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   102  	require.NoError(t, err)
   103  	require.NoError(t, DefaultRPM.Package(exampleInfo(), f))
   104  
   105  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   106  	require.NoError(t, err)
   107  	defer func() {
   108  		f.Close()
   109  		file.Close()
   110  		err = os.Remove(file.Name())
   111  		require.NoError(t, err)
   112  	}()
   113  	rpm, err := rpmutils.ReadRpm(file)
   114  	require.NoError(t, err)
   115  
   116  	os, err := rpm.Header.GetString(rpmutils.OS)
   117  	require.NoError(t, err)
   118  	require.Equal(t, "linux", os)
   119  
   120  	arch, err := rpm.Header.GetString(rpmutils.ARCH)
   121  	require.NoError(t, err)
   122  	require.Equal(t, archToRPM["amd64"], arch)
   123  
   124  	version, err := rpm.Header.GetString(rpmutils.VERSION)
   125  	require.NoError(t, err)
   126  	require.Equal(t, "1.0.0", version)
   127  
   128  	release, err := rpm.Header.GetString(rpmutils.RELEASE)
   129  	require.NoError(t, err)
   130  	require.Equal(t, "1", release)
   131  
   132  	epoch, err := rpm.Header.Get(rpmutils.EPOCH)
   133  	require.NoError(t, err)
   134  	epochUint32, ok := epoch.([]uint32)
   135  	require.True(t, ok)
   136  	require.Len(t, epochUint32, 1)
   137  	require.Equal(t, uint32(0), epochUint32[0])
   138  
   139  	group, err := rpm.Header.GetString(rpmutils.GROUP)
   140  	require.NoError(t, err)
   141  	require.Equal(t, "foo", group)
   142  
   143  	buildhost, err := rpm.Header.GetString(rpmutils.BUILDHOST)
   144  	require.NoError(t, err)
   145  	require.Equal(t, "barhost", buildhost)
   146  
   147  	summary, err := rpm.Header.GetString(rpmutils.SUMMARY)
   148  	require.NoError(t, err)
   149  	require.Equal(t, "Foo does things", summary)
   150  
   151  	description, err := rpm.Header.GetString(rpmutils.DESCRIPTION)
   152  	require.NoError(t, err)
   153  	require.Equal(t, "Foo does things", description)
   154  }
   155  
   156  func TestIssue952(t *testing.T) {
   157  	info := exampleInfo()
   158  	info.MTime = time.Time{}
   159  
   160  	info.Contents = files.Contents{
   161  		&files.Content{
   162  			Source:      "/file-that-does-not-exist",
   163  			Destination: "/etc/link",
   164  			Type:        files.TypeSymlink,
   165  		},
   166  	}
   167  
   168  	var buf bytes.Buffer
   169  	err := DefaultRPM.Package(info, &buf)
   170  	require.NoError(t, err)
   171  
   172  	rpm, err := rpmutils.ReadRpm(&buf)
   173  	require.NoError(t, err)
   174  
   175  	files, err := rpm.Header.GetFiles()
   176  	require.NoError(t, err)
   177  	require.Len(t, files, 1)
   178  	f := files[0]
   179  	require.Equal(t, cpio.S_ISLNK, f.Mode())
   180  	require.Equal(t, "/etc/link", f.Name())
   181  	require.Equal(t, "/file-that-does-not-exist", f.Linkname())
   182  	require.Positive(t, f.Mtime())
   183  }
   184  
   185  func TestSRPM(t *testing.T) {
   186  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   187  	require.NoError(t, err)
   188  	require.NoError(t, DefaultSRPM.Package(exampleInfo(), f))
   189  
   190  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   191  	require.NoError(t, err)
   192  	defer func() {
   193  		f.Close()
   194  		file.Close()
   195  		err = os.Remove(file.Name())
   196  		require.NoError(t, err)
   197  	}()
   198  	rpm, err := rpmutils.ReadRpm(file)
   199  	require.NoError(t, err)
   200  
   201  	os, err := rpm.Header.GetString(rpmutils.OS)
   202  	require.NoError(t, err)
   203  	require.Equal(t, "linux", os)
   204  
   205  	arch, err := rpm.Header.GetString(rpmutils.ARCH)
   206  	require.NoError(t, err)
   207  	require.Equal(t, archToRPM["amd64"], arch)
   208  
   209  	version, err := rpm.Header.GetString(rpmutils.VERSION)
   210  	require.NoError(t, err)
   211  	require.Equal(t, "1.0.0", version)
   212  
   213  	release, err := rpm.Header.GetString(rpmutils.RELEASE)
   214  	require.NoError(t, err)
   215  	require.Equal(t, "1", release)
   216  
   217  	epoch, err := rpm.Header.Get(rpmutils.EPOCH)
   218  	require.NoError(t, err)
   219  	epochUint32, ok := epoch.([]uint32)
   220  	require.True(t, ok)
   221  	require.Len(t, epochUint32, 1)
   222  	require.Equal(t, uint32(0), epochUint32[0])
   223  
   224  	group, err := rpm.Header.GetString(rpmutils.GROUP)
   225  	require.NoError(t, err)
   226  	require.Equal(t, "foo", group)
   227  
   228  	buildhost, err := rpm.Header.GetString(rpmutils.BUILDHOST)
   229  	require.NoError(t, err)
   230  	require.Equal(t, "barhost", buildhost)
   231  
   232  	summary, err := rpm.Header.GetString(rpmutils.SUMMARY)
   233  	require.NoError(t, err)
   234  	require.Equal(t, "Foo does things", summary)
   235  
   236  	description, err := rpm.Header.GetString(rpmutils.DESCRIPTION)
   237  	require.NoError(t, err)
   238  	require.Equal(t, "Foo does things", description)
   239  
   240  	bts, err := rpm.Header.GetUint32s(tagSourcePackage)
   241  	require.NoError(t, err)
   242  	require.Equal(t, []uint32{1}, bts)
   243  }
   244  
   245  func TestRPMMandatoryFieldsOnly(t *testing.T) {
   246  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   247  	require.NoError(t, err)
   248  	require.NoError(t, DefaultRPM.Package(&nfpm.Info{
   249  		Name:        "foo",
   250  		Arch:        "amd64",
   251  		Version:     "1.2",
   252  		Release:     "1",
   253  		Description: "summary\nfoo bar\nlong description",
   254  		License:     "MIT",
   255  	}, f))
   256  
   257  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   258  	require.NoError(t, err)
   259  	defer func() {
   260  		f.Close()
   261  		file.Close()
   262  		err = os.Remove(file.Name())
   263  		require.NoError(t, err)
   264  	}()
   265  	rpm, err := rpmutils.ReadRpm(file)
   266  	require.NoError(t, err)
   267  
   268  	os, err := rpm.Header.GetString(rpmutils.OS)
   269  	require.NoError(t, err)
   270  	require.Equal(t, "linux", os)
   271  
   272  	arch, err := rpm.Header.GetString(rpmutils.ARCH)
   273  	require.NoError(t, err)
   274  	require.Equal(t, archToRPM["amd64"], arch)
   275  
   276  	version, err := rpm.Header.GetString(rpmutils.VERSION)
   277  	require.NoError(t, err)
   278  	require.Equal(t, "1.2", version)
   279  
   280  	release, err := rpm.Header.GetString(rpmutils.RELEASE)
   281  	require.NoError(t, err)
   282  	require.Equal(t, "1", release)
   283  
   284  	_, err = rpm.Header.Get(rpmutils.EPOCH)
   285  	require.Error(t, err, "epoch should not be set")
   286  
   287  	_, err = rpm.Header.GetString(rpmutils.GROUP)
   288  	require.Error(t, err, "group should not be set")
   289  
   290  	summary, err := rpm.Header.GetString(rpmutils.SUMMARY)
   291  	require.NoError(t, err)
   292  	require.Equal(t, "summary", summary)
   293  
   294  	description, err := rpm.Header.GetString(rpmutils.DESCRIPTION)
   295  	require.NoError(t, err)
   296  	require.Equal(t, "summary\nfoo bar\nlong description", description)
   297  }
   298  
   299  func TestRPMPlatform(t *testing.T) {
   300  	f, err := os.CreateTemp(t.TempDir(), "test*.rpm")
   301  	require.NoError(t, err)
   302  	t.Cleanup(func() { require.NoError(t, f.Close()) })
   303  	info := exampleInfo()
   304  	info.Platform = "darwin"
   305  	require.NoError(t, DefaultRPM.Package(info, f))
   306  }
   307  
   308  func TestRPMGroup(t *testing.T) {
   309  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   310  	require.NoError(t, err)
   311  	info := exampleInfo()
   312  	info.RPM.Group = "Unspecified"
   313  	require.NoError(t, DefaultRPM.Package(info, f))
   314  
   315  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   316  	require.NoError(t, err)
   317  	defer func() {
   318  		f.Close()
   319  		file.Close()
   320  		err = os.Remove(file.Name())
   321  		require.NoError(t, err)
   322  	}()
   323  
   324  	rpm, err := rpmutils.ReadRpm(file)
   325  	require.NoError(t, err)
   326  
   327  	group, err := rpm.Header.GetString(rpmutils.GROUP)
   328  	require.NoError(t, err)
   329  	require.Equal(t, "Unspecified", group)
   330  }
   331  
   332  func TestRPMCompression(t *testing.T) {
   333  	for _, compressor := range []string{"gzip", "lzma", "xz", "zstd"} {
   334  		for _, level := range []int{-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9} {
   335  			compLevel := fmt.Sprintf("%v:%v", compressor, level)
   336  			if strings.HasPrefix(compLevel, "xz:") {
   337  				compLevel = "xz"
   338  			}
   339  			if strings.HasPrefix(compLevel, "lzma:") {
   340  				compLevel = "lzma"
   341  			}
   342  			t.Run(compLevel, func(t *testing.T) {
   343  				f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   344  				require.NoError(t, err)
   345  
   346  				info := exampleInfo()
   347  				info.RPM.Compression = compLevel
   348  
   349  				require.NoError(t, DefaultRPM.Package(info, f))
   350  				file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   351  				require.NoError(t, err)
   352  				defer func() {
   353  					f.Close()
   354  					file.Close()
   355  					err = os.Remove(file.Name())
   356  					require.NoError(t, err)
   357  				}()
   358  				rpm, err := rpmutils.ReadRpm(file)
   359  				require.NoError(t, err)
   360  
   361  				rpmCompressor, err := rpm.Header.GetString(rpmutils.PAYLOADCOMPRESSOR)
   362  				require.NoError(t, err)
   363  				require.Equal(t, compressor, rpmCompressor)
   364  			})
   365  			if compLevel == "xz" || compLevel == "lzma" {
   366  				break
   367  			}
   368  		}
   369  	}
   370  }
   371  
   372  func TestRPMSummary(t *testing.T) {
   373  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   374  	require.NoError(t, err)
   375  
   376  	customSummary := "This is my custom summary"
   377  	info := exampleInfo()
   378  	info.RPM.Group = "Unspecified"
   379  	info.RPM.Summary = customSummary
   380  
   381  	require.NoError(t, DefaultRPM.Package(info, f))
   382  
   383  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   384  	require.NoError(t, err)
   385  	defer func() {
   386  		f.Close()
   387  		file.Close()
   388  		err = os.Remove(file.Name())
   389  		require.NoError(t, err)
   390  	}()
   391  	rpm, err := rpmutils.ReadRpm(file)
   392  	require.NoError(t, err)
   393  
   394  	summary, err := rpm.Header.GetString(rpmutils.SUMMARY)
   395  	require.NoError(t, err)
   396  	require.Equal(t, customSummary, summary)
   397  }
   398  
   399  func TestRPMPackager(t *testing.T) {
   400  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   401  	require.NoError(t, err)
   402  
   403  	customPackager := "GoReleaser <staff@goreleaser.com>"
   404  	info := exampleInfo()
   405  	info.RPM.Group = "Unspecified"
   406  	info.RPM.Packager = customPackager
   407  
   408  	require.NoError(t, DefaultRPM.Package(info, f))
   409  
   410  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   411  	require.NoError(t, err)
   412  	defer func() {
   413  		f.Close()
   414  		file.Close()
   415  		err = os.Remove(file.Name())
   416  		require.NoError(t, err)
   417  	}()
   418  	rpm, err := rpmutils.ReadRpm(file)
   419  	require.NoError(t, err)
   420  
   421  	packager, err := rpm.Header.GetString(rpmutils.PACKAGER)
   422  	require.NoError(t, err)
   423  	require.Equal(t, customPackager, packager)
   424  }
   425  
   426  func TestWithRPMTags(t *testing.T) {
   427  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   428  	require.NoError(t, err)
   429  
   430  	info := exampleInfo()
   431  	info.Release = "3"
   432  	info.Epoch = "42"
   433  	info.RPM = nfpm.RPM{
   434  		Group: "default",
   435  	}
   436  	info.Description = "first line\nsecond line\nthird line"
   437  	require.NoError(t, DefaultRPM.Package(info, f))
   438  
   439  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   440  	require.NoError(t, err)
   441  	defer func() {
   442  		f.Close()
   443  		file.Close()
   444  		err = os.Remove(file.Name())
   445  		require.NoError(t, err)
   446  	}()
   447  
   448  	rpm, err := rpmutils.ReadRpm(file)
   449  	require.NoError(t, err)
   450  
   451  	version, err := rpm.Header.GetString(rpmutils.VERSION)
   452  	require.NoError(t, err)
   453  	require.Equal(t, "1.0.0", version)
   454  
   455  	release, err := rpm.Header.GetString(rpmutils.RELEASE)
   456  	require.NoError(t, err)
   457  	require.Equal(t, "3", release)
   458  
   459  	epoch, err := rpm.Header.Get(rpmutils.EPOCH)
   460  	require.NoError(t, err)
   461  	epochUint32, ok := epoch.([]uint32)
   462  	require.Len(t, epochUint32, 1)
   463  	require.True(t, ok)
   464  	require.Equal(t, uint32(42), epochUint32[0])
   465  
   466  	group, err := rpm.Header.GetString(rpmutils.GROUP)
   467  	require.NoError(t, err)
   468  	require.Equal(t, "default", group)
   469  
   470  	summary, err := rpm.Header.GetString(rpmutils.SUMMARY)
   471  	require.NoError(t, err)
   472  	require.Equal(t, "first line", summary)
   473  
   474  	description, err := rpm.Header.GetString(rpmutils.DESCRIPTION)
   475  	require.NoError(t, err)
   476  	require.Equal(t, info.Description, description)
   477  }
   478  
   479  func TestRPMVersion(t *testing.T) {
   480  	info := exampleInfo()
   481  	info.Version = "1.0.0" //nolint:golint,goconst
   482  	meta, err := buildRPMMeta(info)
   483  	require.NoError(t, err)
   484  	require.Equal(t, "1.0.0", meta.Version)
   485  	require.Equal(t, "1", meta.Release)
   486  }
   487  
   488  func TestRPMVersionWithRelease(t *testing.T) {
   489  	info := exampleInfo()
   490  	info.Version = "1.0.0" //nolint:golint,goconst
   491  	info.Release = "2"
   492  	meta, err := buildRPMMeta(info)
   493  	require.NoError(t, err)
   494  	require.Equal(t, "1.0.0", meta.Version)
   495  	require.Equal(t, "2", meta.Release)
   496  }
   497  
   498  func TestRPMVersionWithPrerelease(t *testing.T) {
   499  	// https://fedoraproject.org/wiki/Package_Versioning_Examples#Complex_versioning_examples
   500  	info := exampleInfo()
   501  
   502  	info.Version = "1.0.0"
   503  	info.Prerelease = "rc1" // nolint:goconst
   504  	meta, err := buildRPMMeta(info)
   505  	require.NoError(t, err)
   506  	require.Equal(t, "1.0.0~rc1", meta.Version)
   507  	require.Equal(t, "1", meta.Release)
   508  
   509  	info.Version = "1.0.0~rc1"
   510  	info.Prerelease = ""
   511  	meta, err = buildRPMMeta(info)
   512  	require.NoError(t, err)
   513  	require.Equal(t, "1.0.0~rc1", meta.Version)
   514  	require.Equal(t, "1", meta.Release)
   515  }
   516  
   517  func TestRPMVersionWithPrereleaseWithDashes(t *testing.T) {
   518  	info := exampleInfo()
   519  	info.Version = "1.0.0"
   520  	info.Prerelease = "rc1-alpha-omega" // nolint:goconst
   521  	meta, err := buildRPMMeta(info)
   522  	require.NoError(t, err)
   523  	require.Equal(t, "1.0.0~rc1_alpha_omega", meta.Version)
   524  	require.Equal(t, "1", meta.Release)
   525  }
   526  
   527  func TestRPMVersionWithReleaseAndPrerelease(t *testing.T) {
   528  	// https://fedoraproject.org/wiki/Package_Versioning_Examples#Complex_versioning_examples
   529  	info := exampleInfo()
   530  
   531  	info.Version = "1.0.0"
   532  	info.Release = "2"
   533  	info.Prerelease = "rc1"
   534  	meta, err := buildRPMMeta(info)
   535  	require.NoError(t, err)
   536  	require.Equal(t, "1.0.0~rc1", meta.Version)
   537  	require.Equal(t, "2", meta.Release)
   538  
   539  	info.Version = "1.0.0~rc1"
   540  	info.Release = "3"
   541  	info.Prerelease = ""
   542  	meta, err = buildRPMMeta(info)
   543  	require.NoError(t, err)
   544  	require.Equal(t, "1.0.0~rc1", meta.Version)
   545  	require.Equal(t, "3", meta.Release)
   546  }
   547  
   548  func TestRPMVersionWithVersionMetadata(t *testing.T) {
   549  	// https://fedoraproject.org/wiki/Package_Versioning_Examples#Complex_versioning_examples
   550  	info := exampleInfo()
   551  
   552  	info.Version = "1.0.0+meta"
   553  	info.VersionMetadata = ""
   554  	meta, err := buildRPMMeta(info)
   555  	require.NoError(t, err)
   556  	require.Equal(t, "1.0.0+meta", meta.Version)
   557  	require.Equal(t, "1", meta.Release)
   558  
   559  	info.Version = "1.0.0"
   560  	info.VersionMetadata = "meta"
   561  	info.Release = "10"
   562  	meta, err = buildRPMMeta(nfpm.WithDefaults(info))
   563  	require.NoError(t, err)
   564  	require.Equal(t, "1.0.0+meta", meta.Version)
   565  	require.Equal(t, "10", meta.Release)
   566  }
   567  
   568  func TestWithInvalidEpoch(t *testing.T) {
   569  	f, err := os.CreateTemp(t.TempDir(), "test.rpm")
   570  	defer func() {
   571  		_ = f.Close()
   572  		err = os.Remove(f.Name())
   573  		require.NoError(t, err)
   574  	}()
   575  
   576  	info := exampleInfo()
   577  	info.Release = "3"
   578  	info.Epoch = "-1"
   579  	info.RPM = nfpm.RPM{
   580  		Group: "default",
   581  	}
   582  	info.Description = "first line\nsecond line\nthird line"
   583  	require.Error(t, DefaultRPM.Package(info, f))
   584  }
   585  
   586  func TestRPMScripts(t *testing.T) {
   587  	info := exampleInfo()
   588  	f, err := os.CreateTemp(t.TempDir(), fmt.Sprintf("%s-%s-*.rpm", info.Name, info.Version))
   589  	require.NoError(t, err)
   590  	err = DefaultRPM.Package(info, f)
   591  	require.NoError(t, err)
   592  	file, err := os.OpenFile(f.Name(), os.O_RDONLY, 0o600) //nolint:gosec
   593  	require.NoError(t, err)
   594  	defer func() {
   595  		f.Close()
   596  		file.Close()
   597  		err = os.Remove(file.Name())
   598  		require.NoError(t, err)
   599  	}()
   600  	rpm, err := rpmutils.ReadRpm(file)
   601  	require.NoError(t, err)
   602  
   603  	data, err := rpm.Header.GetString(rpmutils.PREIN)
   604  	require.NoError(t, err)
   605  	require.Equal(t, `#!/bin/bash
   606  
   607  echo "Preinstall" > /dev/null
   608  `, data, "Preinstall script does not match")
   609  
   610  	data, err = rpm.Header.GetString(rpmutils.PREUN)
   611  	require.NoError(t, err)
   612  	require.Equal(t, `#!/bin/bash
   613  
   614  echo "Preremove" > /dev/null
   615  `, data, "Preremove script does not match")
   616  
   617  	data, err = rpm.Header.GetString(rpmutils.POSTIN)
   618  	require.NoError(t, err)
   619  	require.Equal(t, `#!/bin/bash
   620  
   621  echo "Postinstall" > /dev/null
   622  `, data, "Postinstall script does not match")
   623  
   624  	data, err = rpm.Header.GetString(rpmutils.POSTUN)
   625  	require.NoError(t, err)
   626  	require.Equal(t, `#!/bin/bash
   627  
   628  echo "Postremove" > /dev/null
   629  `, data, "Postremove script does not match")
   630  
   631  	rpmPreTransTag := 1151
   632  	data, err = rpm.Header.GetString(rpmPreTransTag)
   633  	require.NoError(t, err)
   634  	require.Equal(t, `#!/bin/bash
   635  
   636  echo "Pretrans" > /dev/null
   637  `, data, "Pretrans script does not match")
   638  
   639  	rpmPostTransTag := 1152
   640  	data, err = rpm.Header.GetString(rpmPostTransTag)
   641  	require.NoError(t, err)
   642  	require.Equal(t, `#!/bin/bash
   643  
   644  echo "Posttrans" > /dev/null
   645  `, data, "Posttrans script does not match")
   646  
   647  	data, err = rpm.Header.GetString(rpmutils.VERIFYSCRIPT)
   648  	require.NoError(t, err)
   649  	require.Equal(t, `#!/bin/bash
   650  
   651  echo "Verify" > /dev/null
   652  `, data, "Verify script does not match")
   653  }
   654  
   655  func TestRPMFileDoesNotExist(t *testing.T) {
   656  	info := exampleInfo()
   657  	info.Contents = []*files.Content{
   658  		{
   659  			Source:      "../testdata/fake",
   660  			Destination: "/usr/bin/fake",
   661  		},
   662  		{
   663  			Source:      "../testdata/whatever.confzzz",
   664  			Destination: "/etc/fake/fake.conf",
   665  			Type:        files.TypeConfig,
   666  		},
   667  	}
   668  	abs, err := filepath.Abs("../testdata/whatever.confzzz")
   669  	require.NoError(t, err)
   670  	err = DefaultRPM.Package(info, io.Discard)
   671  	require.EqualError(t, err, fmt.Sprintf("matching \"%s\": file does not exist", filepath.ToSlash(abs)))
   672  }
   673  
   674  func TestArches(t *testing.T) {
   675  	for k := range archToRPM {
   676  		t.Run(k, func(t *testing.T) {
   677  			info := exampleInfo()
   678  			info.Arch = k
   679  			info = setDefaults(info)
   680  			require.Equal(t, archToRPM[k], info.Arch)
   681  		})
   682  	}
   683  
   684  	t.Run("override", func(t *testing.T) {
   685  		info := exampleInfo()
   686  		info.RPM.Arch = "foo64"
   687  		info = setDefaults(info)
   688  		require.Equal(t, "foo64", info.Arch)
   689  	})
   690  }
   691  
   692  func TestConfigMissingOK(t *testing.T) {
   693  	var (
   694  		buildConfigFile   = "../testdata/whatever.conf"
   695  		packageConfigFile = "/etc/fake/fake.conf"
   696  	)
   697  
   698  	info := &nfpm.Info{
   699  		Name:        "symlink-in-files",
   700  		Arch:        "amd64",
   701  		Description: "This package's config references a file via symlink.",
   702  		Version:     "1.0.0",
   703  		Maintainer:  "maintainer",
   704  		Overridables: nfpm.Overridables{
   705  			Contents: []*files.Content{
   706  				{
   707  					Source:      buildConfigFile,
   708  					Destination: packageConfigFile,
   709  					Type:        files.TypeConfigMissingOK,
   710  				},
   711  			},
   712  		},
   713  	}
   714  
   715  	var rpmFileBuffer bytes.Buffer
   716  	err := DefaultRPM.Package(info, &rpmFileBuffer)
   717  	require.NoError(t, err)
   718  
   719  	expectedConfigContent, err := os.ReadFile(buildConfigFile)
   720  	require.NoError(t, err)
   721  
   722  	packageConfigContent, err := extractFileFromRpm(rpmFileBuffer.Bytes(), packageConfigFile)
   723  	require.NoError(t, err)
   724  
   725  	require.Equal(t, expectedConfigContent, packageConfigContent)
   726  }
   727  
   728  func TestConfigNoReplace(t *testing.T) {
   729  	var (
   730  		buildConfigFile   = "../testdata/whatever.conf"
   731  		packageConfigFile = "/etc/fake/fake.conf"
   732  	)
   733  
   734  	info := &nfpm.Info{
   735  		Name:        "symlink-in-files",
   736  		Arch:        "amd64",
   737  		Description: "This package's config references a file via symlink.",
   738  		Version:     "1.0.0",
   739  		Maintainer:  "maintainer",
   740  		Overridables: nfpm.Overridables{
   741  			Contents: []*files.Content{
   742  				{
   743  					Source:      buildConfigFile,
   744  					Destination: packageConfigFile,
   745  					Type:        files.TypeConfigNoReplace,
   746  				},
   747  			},
   748  		},
   749  	}
   750  
   751  	var rpmFileBuffer bytes.Buffer
   752  	err := DefaultRPM.Package(info, &rpmFileBuffer)
   753  	require.NoError(t, err)
   754  
   755  	expectedConfigContent, err := os.ReadFile(buildConfigFile)
   756  	require.NoError(t, err)
   757  
   758  	packageConfigContent, err := extractFileFromRpm(rpmFileBuffer.Bytes(), packageConfigFile)
   759  	require.NoError(t, err)
   760  
   761  	require.Equal(t, expectedConfigContent, packageConfigContent)
   762  }
   763  
   764  func TestRPMConventionalFileName(t *testing.T) {
   765  	info := &nfpm.Info{
   766  		Name:       "testpkg",
   767  		Arch:       "noarch",
   768  		Maintainer: "maintainer",
   769  	}
   770  
   771  	testCases := []struct {
   772  		Version    string
   773  		Release    string
   774  		Prerelease string
   775  		Expected   string
   776  		Metadata   string
   777  	}{
   778  		{
   779  			Version: "1.2.3", Release: "", Prerelease: "", Metadata: "",
   780  			Expected: fmt.Sprintf("%s-1.2.3-1.%s.rpm", info.Name, info.Arch),
   781  		},
   782  		{
   783  			Version: "1.2.3", Release: "4", Prerelease: "", Metadata: "",
   784  			Expected: fmt.Sprintf("%s-1.2.3-4.%s.rpm", info.Name, info.Arch),
   785  		},
   786  		{
   787  			Version: "1.2.3", Release: "4", Prerelease: "5", Metadata: "",
   788  			Expected: fmt.Sprintf("%s-1.2.3~5-4.%s.rpm", info.Name, info.Arch),
   789  		},
   790  		{
   791  			Version: "1.2.3", Release: "", Prerelease: "5", Metadata: "",
   792  			Expected: fmt.Sprintf("%s-1.2.3~5-1.%s.rpm", info.Name, info.Arch),
   793  		},
   794  		{
   795  			Version: "1.2.3", Release: "1", Prerelease: "5", Metadata: "git",
   796  			Expected: fmt.Sprintf("%s-1.2.3~5+git-1.%s.rpm", info.Name, info.Arch),
   797  		},
   798  	}
   799  
   800  	for _, testCase := range testCases {
   801  		info.Version = testCase.Version
   802  		info.Release = testCase.Release
   803  		info.Prerelease = testCase.Prerelease
   804  		info.VersionMetadata = testCase.Metadata
   805  
   806  		require.Equal(t, testCase.Expected, DefaultRPM.ConventionalFileName(info))
   807  	}
   808  }
   809  
   810  func TestRPMChangelog(t *testing.T) {
   811  	info := exampleInfo()
   812  	info.Changelog = "../testdata/changelog.yaml"
   813  
   814  	var rpmFileBuffer bytes.Buffer
   815  	err := DefaultRPM.Package(info, &rpmFileBuffer)
   816  	require.NoError(t, err)
   817  
   818  	rpm, err := rpmutils.ReadRpm(bytes.NewReader(rpmFileBuffer.Bytes()))
   819  	require.NoError(t, err)
   820  
   821  	changelog, err := chglog.Parse(info.Changelog)
   822  	require.NoError(t, err)
   823  
   824  	_times, err := rpm.Header.Get(tagChangelogTime)
   825  	require.NoError(t, err)
   826  	times, ok := _times.([]uint32)
   827  	require.True(t, ok)
   828  	require.Len(t, changelog, len(times))
   829  
   830  	_titles, err := rpm.Header.Get(tagChangelogName)
   831  	require.NoError(t, err)
   832  	titles, ok := _titles.([]string)
   833  	require.True(t, ok)
   834  	require.Len(t, changelog, len(titles))
   835  
   836  	_notes, err := rpm.Header.Get(tagChangelogText)
   837  	require.NoError(t, err)
   838  	allNotes, ok := _notes.([]string)
   839  	require.True(t, ok)
   840  	require.Len(t, changelog, len(allNotes))
   841  
   842  	for i, entry := range changelog {
   843  		timestamp := time.Unix(int64(times[i]), 0).UTC()
   844  		title := titles[i]
   845  		notes := strings.Split(allNotes[i], "\n")
   846  
   847  		require.Equal(t, entry.Date, timestamp)
   848  		require.Contains(t, title, entry.Packager)
   849  		require.Contains(t, title, entry.Semver)
   850  		require.Len(t, entry.Changes, len(notes))
   851  
   852  		for j, change := range entry.Changes {
   853  			require.Contains(t, notes[j], change.Note)
   854  		}
   855  	}
   856  }
   857  
   858  func TestRPMNoChangelogTagsWithoutChangelogConfigured(t *testing.T) {
   859  	info := exampleInfo()
   860  
   861  	var rpmFileBuffer bytes.Buffer
   862  	err := DefaultRPM.Package(info, &rpmFileBuffer)
   863  	require.NoError(t, err)
   864  
   865  	rpm, err := rpmutils.ReadRpm(bytes.NewReader(rpmFileBuffer.Bytes()))
   866  	require.NoError(t, err)
   867  
   868  	_, err = rpm.Header.Get(tagChangelogTime)
   869  	require.Error(t, err)
   870  
   871  	_, err = rpm.Header.Get(tagChangelogName)
   872  	require.Error(t, err)
   873  
   874  	_, err = rpm.Header.Get(tagChangelogText)
   875  	require.Error(t, err)
   876  }
   877  
   878  func TestSymlink(t *testing.T) {
   879  	var (
   880  		configFilePath = "/usr/share/doc/fake/fake.txt"
   881  		symlink        = "/path/to/symlink"
   882  		symlinkTarget  = configFilePath
   883  	)
   884  
   885  	info := &nfpm.Info{
   886  		Name:        "symlink-in-files",
   887  		Arch:        "amd64",
   888  		Description: "This package's config references a file via symlink.",
   889  		Version:     "1.0.0",
   890  		Maintainer:  "maintainer",
   891  		Overridables: nfpm.Overridables{
   892  			Contents: []*files.Content{
   893  				{
   894  					Source:      "../testdata/whatever.conf",
   895  					Destination: configFilePath,
   896  				},
   897  				{
   898  					Source:      symlinkTarget,
   899  					Destination: symlink,
   900  					Type:        files.TypeSymlink,
   901  				},
   902  			},
   903  		},
   904  	}
   905  
   906  	var rpmFileBuffer bytes.Buffer
   907  	err := DefaultRPM.Package(info, &rpmFileBuffer)
   908  	require.NoError(t, err)
   909  
   910  	packagedSymlinkHeader, err := extractFileHeaderFromRpm(rpmFileBuffer.Bytes(), symlink)
   911  	require.NoError(t, err)
   912  
   913  	packagedSymlink, err := extractFileFromRpm(rpmFileBuffer.Bytes(), symlink)
   914  	require.NoError(t, err)
   915  
   916  	require.Equal(t, symlink, packagedSymlinkHeader.Filename())
   917  	require.Equal(t, cpio.S_ISLNK, packagedSymlinkHeader.Mode())
   918  	require.Equal(t, symlinkTarget, string(packagedSymlink))
   919  }
   920  
   921  func TestRPMSignature(t *testing.T) {
   922  	info := exampleInfo()
   923  	info.RPM.Signature.KeyFile = "../internal/sign/testdata/privkey.asc"
   924  	info.RPM.Signature.KeyPassphrase = "hunter2"
   925  
   926  	pubkeyFileContent, err := os.ReadFile("../internal/sign/testdata/pubkey.gpg")
   927  	require.NoError(t, err)
   928  
   929  	keyring, err := openpgp.ReadKeyRing(bytes.NewReader(pubkeyFileContent))
   930  	require.NoError(t, err)
   931  	require.NotNil(t, keyring, "cannot verify sigs with an empty keyring")
   932  
   933  	var rpmBuffer bytes.Buffer
   934  	err = DefaultRPM.Package(info, &rpmBuffer)
   935  	require.NoError(t, err)
   936  
   937  	_, sigs, err := rpmutils.Verify(bytes.NewReader(rpmBuffer.Bytes()), keyring)
   938  	require.NoError(t, err)
   939  	require.Len(t, sigs, 2)
   940  }
   941  
   942  func TestRPMSignatureError(t *testing.T) {
   943  	info := exampleInfo()
   944  	info.RPM.Signature.KeyFile = "../internal/sign/testdata/privkey.asc"
   945  	info.RPM.Signature.KeyPassphrase = "wrongpass"
   946  
   947  	var rpmBuffer bytes.Buffer
   948  	err := DefaultRPM.Package(info, &rpmBuffer)
   949  	require.Error(t, err)
   950  
   951  	var expectedError *nfpm.ErrSigningFailure
   952  	require.ErrorAs(t, err, &expectedError)
   953  }
   954  
   955  func TestRPMSignatureCallback(t *testing.T) {
   956  	info := exampleInfo()
   957  	info.RPM.Signature.SignFn = func(r io.Reader) ([]byte, error) {
   958  		data, err := io.ReadAll(r)
   959  		if err != nil {
   960  			return nil, err
   961  		}
   962  		return sign.PGPSignerWithKeyID("../internal/sign/testdata/privkey.asc", "hunter2", nil)(data)
   963  	}
   964  
   965  	pubkeyFileContent, err := os.ReadFile("../internal/sign/testdata/pubkey.gpg")
   966  	require.NoError(t, err)
   967  
   968  	keyring, err := openpgp.ReadKeyRing(bytes.NewReader(pubkeyFileContent))
   969  	require.NoError(t, err)
   970  	require.NotNil(t, keyring, "cannot verify sigs with an empty keyring")
   971  
   972  	var rpmBuffer bytes.Buffer
   973  	err = DefaultRPM.Package(info, &rpmBuffer)
   974  	require.NoError(t, err)
   975  
   976  	_, sigs, err := rpmutils.Verify(bytes.NewReader(rpmBuffer.Bytes()), keyring)
   977  	require.NoError(t, err)
   978  	require.Len(t, sigs, 2)
   979  }
   980  
   981  func TestRPMGhostFiles(t *testing.T) {
   982  	filename := "/usr/lib/casper.a"
   983  
   984  	info := &nfpm.Info{
   985  		Name:        "rpm-ghost",
   986  		Arch:        "amd64",
   987  		Description: "This RPM contains ghost files.",
   988  		Version:     "1.0.0",
   989  		Maintainer:  "maintainer",
   990  		Overridables: nfpm.Overridables{
   991  			Contents: []*files.Content{
   992  				{
   993  					Destination: filename,
   994  					Type:        files.TypeRPMGhost,
   995  				},
   996  			},
   997  		},
   998  	}
   999  
  1000  	var rpmFileBuffer bytes.Buffer
  1001  	err := DefaultRPM.Package(info, &rpmFileBuffer)
  1002  	require.NoError(t, err)
  1003  
  1004  	headerFiles, err := extraFileInfoSliceFromRpm(rpmFileBuffer.Bytes())
  1005  	require.NoError(t, err)
  1006  
  1007  	type headerFileInfo struct {
  1008  		Name string
  1009  		Size int64
  1010  		Mode int
  1011  	}
  1012  	expected := []headerFileInfo{
  1013  		{filename, 0, cpio.S_ISREG | 0o644},
  1014  	}
  1015  	actual := make([]headerFileInfo, 0)
  1016  	for _, fileInfo := range headerFiles {
  1017  		actual = append(actual, headerFileInfo{fileInfo.Name(), fileInfo.Size(), fileInfo.Mode()})
  1018  	}
  1019  	require.Equal(t, expected, actual)
  1020  
  1021  	_, err = extractFileHeaderFromRpm(rpmFileBuffer.Bytes(), filename)
  1022  	require.Error(t, err)
  1023  
  1024  	_, err = extractFileFromRpm(rpmFileBuffer.Bytes(), filename)
  1025  	require.Error(t, err)
  1026  }
  1027  
  1028  func TestDisableGlobbing(t *testing.T) {
  1029  	info := exampleInfo()
  1030  	info.DisableGlobbing = true
  1031  	info.Contents = []*files.Content{
  1032  		{
  1033  			Source:      "../testdata/{file}[",
  1034  			Destination: "/test/{file}[",
  1035  		},
  1036  	}
  1037  
  1038  	var rpmFileBuffer bytes.Buffer
  1039  	err := DefaultRPM.Package(info, &rpmFileBuffer)
  1040  	require.NoError(t, err)
  1041  
  1042  	expectedContent, err := os.ReadFile("../testdata/{file}[")
  1043  	require.NoError(t, err)
  1044  
  1045  	actualContent, err := extractFileFromRpm(rpmFileBuffer.Bytes(), "/test/{file}[")
  1046  	require.NoError(t, err)
  1047  
  1048  	require.Equal(t, expectedContent, actualContent)
  1049  }
  1050  
  1051  func TestDirectories(t *testing.T) {
  1052  	info := exampleInfo()
  1053  	info.Contents = []*files.Content{
  1054  		{
  1055  			Source:      "../testdata/whatever.conf",
  1056  			Destination: "/etc/foo/file",
  1057  		},
  1058  		{
  1059  			Source:      "../testdata/whatever.conf",
  1060  			Destination: "/etc/bar/file",
  1061  		},
  1062  		{
  1063  			Destination: "/etc/bar",
  1064  			Type:        files.TypeDir,
  1065  		},
  1066  		{
  1067  			Destination: "/etc/baz",
  1068  			Type:        files.TypeDir,
  1069  			FileInfo: &files.ContentFileInfo{
  1070  				Owner: "test",
  1071  				Mode:  0o700,
  1072  			},
  1073  		},
  1074  		{
  1075  			Destination: "/usr/lib/something/somethingelse",
  1076  			Type:        files.TypeDir,
  1077  		},
  1078  	}
  1079  
  1080  	var rpmFileBuffer bytes.Buffer
  1081  	err := DefaultRPM.Package(info, &rpmFileBuffer)
  1082  	require.NoError(t, err)
  1083  
  1084  	require.Equal(t, []string{
  1085  		"/etc/bar",
  1086  		"/etc/bar/file",
  1087  		"/etc/baz",
  1088  		"/etc/foo/file",
  1089  		"/usr/lib/something/somethingelse",
  1090  	}, getTree(t, rpmFileBuffer.Bytes()))
  1091  
  1092  	// the directory /etc/foo should not be implicitly created as that
  1093  	// implies ownership of /etc/foo which should always be implicit
  1094  	_, err = extractFileHeaderFromRpm(rpmFileBuffer.Bytes(), "/etc/foo")
  1095  	require.Equal(t, err, os.ErrNotExist)
  1096  
  1097  	// claiming explicit ownership of /etc/bar which already contains a file
  1098  	h, err := extractFileHeaderFromRpm(rpmFileBuffer.Bytes(), "/etc/bar")
  1099  	require.NoError(t, err)
  1100  	require.NotEqual(t, 0, h.Mode()&int(tagDirectory))
  1101  
  1102  	// creating an empty folder (which also implies ownership)
  1103  	h, err = extractFileHeaderFromRpm(rpmFileBuffer.Bytes(), "/etc/baz")
  1104  	require.NoError(t, err)
  1105  	require.Equal(t, h.Mode(), int(tagDirectory|0o700))
  1106  }
  1107  
  1108  func TestGlob(t *testing.T) {
  1109  	require.NoError(t, DefaultRPM.Package(nfpm.WithDefaults(&nfpm.Info{
  1110  		Name:       "nfpm-repro",
  1111  		Version:    "1.0.0",
  1112  		Maintainer: "asdfasdf",
  1113  
  1114  		Overridables: nfpm.Overridables{
  1115  			Contents: files.Contents{
  1116  				{
  1117  					Destination: "/usr/share/nfpm-repro",
  1118  					Source:      "../files/*",
  1119  				},
  1120  			},
  1121  		},
  1122  	}), io.Discard))
  1123  }
  1124  
  1125  func TestIgnoreUnrelatedFiles(t *testing.T) {
  1126  	info := exampleInfo()
  1127  	info.Contents = files.Contents{
  1128  		{
  1129  			Source:      "../testdata/fake",
  1130  			Destination: "/some/file",
  1131  			Type:        files.TypeDebChangelog,
  1132  		},
  1133  	}
  1134  
  1135  	var rpmFileBuffer bytes.Buffer
  1136  	err := DefaultRPM.Package(info, &rpmFileBuffer)
  1137  	require.NoError(t, err)
  1138  
  1139  	require.Empty(t, getTree(t, rpmFileBuffer.Bytes()))
  1140  }
  1141  
  1142  func extractFileFromRpm(rpm []byte, filename string) ([]byte, error) {
  1143  	rpmFile, err := rpmutils.ReadRpm(bytes.NewReader(rpm))
  1144  	if err != nil {
  1145  		return nil, err
  1146  	}
  1147  
  1148  	pr, err := rpmFile.PayloadReader()
  1149  	if err != nil {
  1150  		return nil, err
  1151  	}
  1152  
  1153  	for {
  1154  		hdr, err := pr.Next()
  1155  		if errors.Is(err, io.EOF) {
  1156  			break // End of archive
  1157  		}
  1158  		if err != nil {
  1159  			return nil, err
  1160  		}
  1161  
  1162  		if hdr.Filename() != filename {
  1163  			continue
  1164  		}
  1165  
  1166  		fileContents, err := io.ReadAll(pr)
  1167  		if err != nil {
  1168  			return nil, err
  1169  		}
  1170  
  1171  		return fileContents, nil
  1172  	}
  1173  
  1174  	return nil, os.ErrNotExist
  1175  }
  1176  
  1177  func extraFileInfoSliceFromRpm(rpm []byte) ([]rpmutils.FileInfo, error) {
  1178  	rpmFile, err := rpmutils.ReadRpm(bytes.NewReader(rpm))
  1179  	if err != nil {
  1180  		return nil, err
  1181  	}
  1182  	return rpmFile.Header.GetFiles()
  1183  }
  1184  
  1185  func getTree(tb testing.TB, rpm []byte) []string {
  1186  	tb.Helper()
  1187  
  1188  	rpmFile, err := rpmutils.ReadRpm(bytes.NewReader(rpm))
  1189  	require.NoError(tb, err)
  1190  	pr, err := rpmFile.PayloadReader()
  1191  	require.NoError(tb, err)
  1192  
  1193  	var tree []string
  1194  	for {
  1195  		hdr, err := pr.Next()
  1196  		if errors.Is(err, io.EOF) {
  1197  			break // End of archive
  1198  		}
  1199  		require.NoError(tb, err)
  1200  		tree = append(tree, hdr.Filename())
  1201  	}
  1202  
  1203  	return tree
  1204  }
  1205  
  1206  func extractFileHeaderFromRpm(rpm []byte, filename string) (*cpio.Cpio_newc_header, error) {
  1207  	rpmFile, err := rpmutils.ReadRpm(bytes.NewReader(rpm))
  1208  	if err != nil {
  1209  		return nil, err
  1210  	}
  1211  
  1212  	pr, err := rpmFile.PayloadReader()
  1213  	if err != nil {
  1214  		return nil, err
  1215  	}
  1216  
  1217  	for {
  1218  		hdr, err := pr.Next()
  1219  		if errors.Is(err, io.EOF) {
  1220  			break // End of archive
  1221  		}
  1222  		if err != nil {
  1223  			return nil, err
  1224  		}
  1225  
  1226  		if hdr.Filename() != filename {
  1227  			continue
  1228  		}
  1229  
  1230  		return hdr, nil
  1231  	}
  1232  
  1233  	return nil, os.ErrNotExist
  1234  }