goreleaser / nfpm

nFPM is Not FPM - a simple deb, rpm, apk, ipk, and arch linux packager written in Go
https://nfpm.goreleaser.com/
MIT License
2.13k stars 157 forks source link

`nfpm` does not ensure reproducible builds #744

Closed keliramu closed 8 months ago

keliramu commented 9 months ago

What happened?

Implemented sample nfpm template to create deb package (as a bash script to create deb package). Executed above mentioned script in a loop of 9 iterations: create package, check size, remove package. Getting stable results: at least 2 times out of 9 deb size differs from previous iteration.

~/nfpm-consistent-results-not$ ./test-deb-size.sh 
Create DEB multiple times to check if resulting size stays the same
Create DEB in loop: 1..9
DEB i: 1; now DEB size: 862
DEB i: 2; now DEB size: 864 ::: GOT different size!!! prev: [862]
DEB i: 3; now DEB size: 862 ::: GOT different size!!! prev: [864]
DEB i: 4; now DEB size: 864 ::: GOT different size!!! prev: [862]
DEB i: 5; now DEB size: 864
DEB i: 6; now DEB size: 864
DEB i: 7; now DEB size: 864
DEB i: 8; now DEB size: 864
DEB i: 9; now DEB size: 864
Done. Got diffs: 3

How can we reproduce this?

Here is the public github repo with scripts to reproduce this problem: https://github.com/keliramu/nfpm-consistent-results-not

nfpm version

nfpm --version
       _____ ____  __  __
 _ __ |  ___|  _ \|  \/  |
| '_ \| |_  | |_) | |\/| |
| | | |  _| |  __/| |  | |
|_| |_|_|   |_|   |_|  |_|
nfpm: a simple and 0-dependencies deb, rpm, apk and arch linux packager written in Go
https://nfpm.goreleaser.com

GitVersion:    2.34.0
GitCommit:     6a16fcd38aa41ec2a5fea50728d22258ab9e2580
GitTreeState:  false
BuildDate:     2023-10-23T11:26:52Z
BuiltBy:       goreleaser
GoVersion:     go1.21.3
Compiler:      gc
ModuleSum:     h1:fKoHucBOcmW2CkIDj3gZZ4grJGRRoed7eRzAztWa3xo=
Platform:      linux/amd64

Search

Code of Conduct

Additional context

No response

caarlos0 commented 8 months ago

I can repro it, but no idea (yet) of what's causing it

CleanShot 2023-12-04 at 23 14 14@2x

caarlos0 commented 8 months ago

seems to be related to the gzip compression...

caarlos0 commented 8 months ago

yup, its the gz compression, changing to xz fixes it:

# ...
contents:
  - src: ./foo111.sh
    dst: /usr/bin/foo111.sh
deb:
  compression: xz # <---- add this
overrides:
  rpm:
    scripts:
# ...

and I don't know if there's much we can do about it.

FWIW, I can kinda repro it with gzip too:

for i in $(seq 1 10); do
  echo 'hello world'>test$i.txt
  gzip test$i.txt
  md5sum test$i.txt.gz
done

I don't know enough of how gzip works though, maybe I'm still missing something

keliramu commented 8 months ago

But this is consistent:

for i in $(seq 1 10); do
  trg_dir="cc"$i
  mkdir $trg_dir
  echo 'hello world'>$trg_dir/test.txt
  gzip $trg_dir/test.txt
  md5sum $trg_dir/test.txt.gz
done
keliramu commented 8 months ago

In this PR https://github.com/goreleaser/nfpm/pull/747 I have solution how to make target deb package stable size. Variation of target deb file is caused by:

  1. random file sort order of files in control.tar
  2. file ModTime attribute

Please review it. Probably need some kind the control of ModTime setting either to chose zero time to ensure reproducible result or keep original mod time.

keliramu commented 8 months ago

BTW, proposed option:

deb:
  compression: xz # <---- add this

Does not solve the problem, as it is only for data.tar but control.tar is always gziped without possibility to chose different method.

caarlos0 commented 8 months ago

ahh, good find, I totally missed that

caarlos0 commented 8 months ago

check #748