REPROTEST(1)
๐ฆ reprotest(1) โ User Commands ๐ฆ
๐ธ NAME ๐ธ
Okay so reprotest is the icon that builds packages and checks them for reproducibility. Periodt. ๐
โจ SYNOPSIS โจ
Hereโs the vibe check of how you call it:
reprotest --help [<virtual_server_name>]
reprotest [options] [-c <build_command>] <source_root> [<artifact_pattern>] [-- <virtual_server_args> [<virtual_server_args> ...]]
reprotest [options] [-s <source_root>] <build_command> [<artifact_pattern>] [-- <virtual_server_args> [<virtual_server_args> ...]]
""
๐ DESCRIPTION ๐
Bestie, reprotest literally builds the same source code twice in different environments, then checks the binaries produced by each build for differences. If it finds any, then diffoscope(1) (or if unavailable then diff(1)) is used to display them in detail for later analysis. Slay. ๐ฅ
Scroll down to the COMMAND-LINE EXAMPLES section to get started, plus there are more detailed explanations of all the command-line options. The same information is also available in /usr/share/doc/reprotest/README.rst or similar. ๐ซถ
๐ง source_root|build_command ๐ง
The first argument is treated either as a source_root (see the -s option) or as a build-command (see the -c option) depending on what it looks like. Most of the time, this should "just work"; but specifically: if neither -c nor -s are given, then: if this exists as a file or directory and is not "auto", then this is treated as a source_root, else as a build_command. Otherwise, if one of -c or -s is given, then this is treated as the other one. If both are given, then this is a syntax error and we exit code 2. ๐ญ
๐ artifact_pattern ๐
Build artifact to test for reproducibility. May be a shell pattern such as "*.deb *.changes". โจ
๐ฆ virtual_server_args ๐ฆ
Arguments to pass to the virtual_server, the first argument being the name of the server. If this itself contains options (of the form -xxx or --xxx), or if any of the previous arguments are omitted, you should put a -- between these arguments and reprotestโs own options. Default: "null", to run directly in /tmp. Choices: chroot, lxc, lxd, null, qemu, schroot, ssh. ๐
๐ ๏ธ options: ๐ ๏ธ
--help [VIRTUAL_SERVER_NAME]โ Show this help message and exit. When given an argument, show instead the help message for that virtual server and exit. ๐-f,--config-file CONFIG_FILEโ File to load configuration from. (Default:None)
๐ basic options: ๐
--verbosity VERBOSITYโ An integer. Control which messages are displayed:0is quiet (warning/error only),1is info,2is debug. ๐-v,--verboseโ Like--verbosity, but given multiple times without arguments. ๐ซถ--host-distro HOST_DISTROโ The distribution that will run the tests. (Default:None)-s,--source-root PATHโ Root of the source tree, that is copied to the virtual server and made available during the build. If a file is given here, then its parent directory is used instead. Default:"."(current working directory). ๐ธ--source-pattern PATTERNSโ Shell glob pattern to restrict the files in<source_root>that are made available during the build. Default: empty, i.e. copy the whole<source_root>directory with no restrictions. ๐ฆ-c,--build-command COMMANDSโ Build command to execute. If this is"auto"then reprotest will guess how to build the givensource_root, in which case various other options may be automaticallyset-if-unset. Default:auto. โญ--store-dir DIRECTORYโ Save the artifacts in this directory, which must be empty or non-existent. Otherwise, the artifacts will be deleted and you only see their hashes (if reproducible) or the diff output (if not). See also--no-clean-on-error. ๐พ--variations VARIATIONSโ Build variations to test as a comma-separated list of variation names. Default is"+all", equivalent to"+environment, +build_path, +kernel, +aslr, +num_cpus, +time, +user_group, +fileordering, +domain_host, +home, +locales, +exec_path, +timezone, +umask", testing all available variations. See the man page section on VARIATIONS for more advanced syntax options, including tweaking how certain variations work. ๐--vary VARIATIONSโ Like--variations, but appends to previous--varyvalues instead of overwriting them. The last value set for--variationsis treated implicitly as the zeroth--varyvalue. ๐--extra-build VARIATIONSโ Perform another build with the givenVARIATIONS(which may be empty) to be applied on top of what was given for--variationsand--vary. Each occurrence of this flag specifies another build, so e.g. given twice this will make reprotest perform 4 builds in total. ๐--auto-buildโ Automatically perform builds to try to determine which specific variations cause unreproducibility, potentially up to and including the ones specified by--variationsand--vary. Conflicts with--extra-build. ๐ฅ--env-buildโ Automatically perform builds to try to determine which specific environment variables cause unreproducibility, based on a hard-coded whitelist and blacklist. You probably want to set--vary=-allas well when setting this flag; see the man page for details. Conflicts with--extra-buildand--autobuild. ๐ซถ--min-cpus NUMโ Minimum CPUs to use when fixingnum_cpus. Default:1. ๐
๐ diff options: ๐
--diffoscope-arg ARGโ Give extra arguments to diffoscope when running it. Default:['--exclude-directory-metadata=yes']. Arguments are{}-formatted with:{0}the name of each experiment run, and{1}the path of the experiment output. โจ--diffoscope PATHโ Path todiffoscope(1). Default:diffoscope. ๐ฆ--no-diffoscopeโ Donโt run diffoscope; instead rundiff(1). Useful if you donโt want to install diffoscope and/or just want a quick answer on whether the reproduction was successful or not, without spending time to compute all the detailed differences. ๐
๐ง advanced options: ๐ง
--testbed-pre COMMANDSโ Shell commands to run before starting the test bed, in the context of the current system environment. This may be used to e.g. compute information needed by the build, where the computation needs packages you donโt want installed in the testbed itself. ๐--testbed-init COMMANDSโ Shell commands to run after starting the test bed, before running anything else. Used to e.g. installdisorderfsin a chroot. ๐ธ--testbed-build-pre COMMANDSโ Shell commands to run before each build, even before applying variations for that build. Used to e.g. install build-dependencies. โญ--auto-preset-expr PYTHON_EXPRESSIONโ This may be used to transform the presets returned by the auto-detection feature. The value should be a python expression that transforms the_variable, which is of typereprotest.presets.ReprotestPreset. See that classโs documentation for ways you can write this expression. Default:_. ๐--no-clean-on-errorโ Donโt clean thevirtual_serverif there was an error. Useful for debugging but will leave cruft on your system depending on thevirtual_serverused; we hint about some but there may be others. ๐ญ--dry-runโ Donโt run the builds, just print what would happen. ๐ซถ--print-sudoersโ Print a sudoers file for passwordless operation using the given--variations, useful foruser_group.available,domain_host.use_sudo. ๐--control-build CONTROL_BUILDโ Override control build with artifacts located on this path. Allows reusing previous build as baseline. ๐--append-build-command COMMANDSโ Append arguments to the build command. โจ
๐ COMMAND-LINE EXAMPLES ๐
The easiest way to run reprotest is via our presets:
# Build the current directory in a null server (/tmp)
$ reprotest .
$ reprotest . -vv -- null -d # for very verbose output
# Build a make-based program
$ reprotest "make clean && make" mybinary
# Build a Debian package
$ apt-get source hello && cd hello-2.10
$ reprotest auto -- null
# Build a Debian package and disable some variations
$ reprotest --vary=-user_group,-domain_host,-fileordering auto -- null
# Build the given Debian source package in an schroot
# See https://wiki.debian.org/sbuild for instructions on setting that up.
$ reprotest reprotest_0.3.3.dsc -- schroot unstable-amd64-sbuild
# Build the given RPM source package
# Only null server (/tmp) is currently supported.
$ reprotest reprotest-0.7.16.src.rpm
# Build the given RPM source package and automatically install dependencies
$ reprotest --testbed-build-pre 'dnf -y builddep ./*.src.rpm' reprotest-0.7.16.src.rpm
Currently, we only support this for Debian and RPM based packages, but are keen on adding more. If we donโt have knowledge on how to build your file or directory, you can send a patch to us on adding this intelligence โ see the reprotest.presets python module, and adapt the existing logic. ๐ ๏ธ
In the meantime, you can use other parts of the CLI to build arbitrary things. Youโll need to give two mandatory arguments, the build command to run and the build artifact file/pattern to test after running the build. For example:
$ reprotest 'python3 setup.py bdist' 'dist/*.tar.gz'
This runs the command on ., the current working directory. To run it on a project located elsewhere:
$ reprotest -s ../path/to/other/project 'python3 setup.py bdist' 'dist/*.tar.gz'
$ reprotest -c 'python3 setup.py bdist' ../path/to/other/project 'dist/*.tar.gz'
These two invocations are equivalent; you can pick the most convenient one for your use-case. When using these from a shell:
- If the build command has spaces, you will need to quote them, e.g.
reprotest "dpkg-buildpackage -b --no-sign" [..]. ๐ - If you want to use several build artifact patterns, or if you want to use shell wildcards as a pattern, you will also need to quote them, e.g.
reprotest [..] "*.tar.gz *.tar.xz". ๐ธ - If your build artifacts have spaces in their names, you will need to quote these twice, e.g.
'"a file with spaces.gz"'for a single artifact or'"dir 1"/* "dir 2"/*'for multiple patterns. ๐
To get more help for the CLI, including documentation on optional arguments and what they do, run:
$ reprotest --help
๐ฅ๏ธ RUNNING IN A VIRTUAL SERVER
okay so plot twist: you can ALSO run the build inside what's called a "virtual server" ๐ โ think a container, a chroot, etc. You run them like this:
$ reprotest 'python3 setup.py bdist_wheel' 'dist/*.whl' -- qemu /path/to/qemu.img
$ reprotest 'dpkg-buildpackage -b --no-sign' '../*.deb' -- schroot unstable-amd64
There are different server types available. Peep --help for a list of them โ it shows up near the top, in the virtual_server_args part of the positional arguments section โจ
For each virtual server (say schroot), you can see which extra arguments it supports:
$ reprotest --help schroot
When you're running builds inside a virtual server, you will probably have to give extra commands, because you need to set up your build dependencies inside the virtual server. For example, here's what the "Debian directory" preset would look like if we ran it using the full CLI ๐
# "Debian directory" preset
$ reprotest . -- schroot unstable-amd64-sbuild
# This is exactly equivalent to this:
$ reprotest -c auto . -- schroot unstable-amd64-sbuild
# In the non-preset full CLI, this is roughly similar to:
$ reprotest \
--testbed-init 'apt-get -y --no-install-recommends install \
disorderfs faketime locales-all sudo util-linux; \
test -c /dev/fuse || mknod -m 666 /dev/fuse c 10 229; \
test -f /etc/mtab || ln -s ../proc/self/mounts /etc/mtab' \
--testbed-build-pre 'apt-get -y --no-install-recommends build-dep ./' \
--build-command 'dpkg-buildpackage --no-sign -b' \
. \
'../*.deb' \
-- \
schroot unstable-amd64-sbuild
The --testbed-init argument is needed to set up the basic tools that reprotest needs in order to make the variations in the first place. This should be the same regardless of what package is being built, but it might differ depending on what virtual_server is being used ๐
Next up we have --testbed-build-pre, then --build-command (or -c). For our Debian directory, we install build-dependencies using apt-get, then we run the actual build command itself using dpkg-buildpackage(1).
Then we have the source_root and the artifact_pattern. For reproducibility, we're only interested in the binary packages ๐
Finally, we specify that this is to take place in the schroot virtual_server with arguments unstable-amd64-sbuild.
Of course, all of this is a total burden to remember if you have to run the same thing many times ๐ญ so that is exactly why adding new presets for new package types would be a good thing.
Here's a more complex example ๐ฅ It tells reprotest to store the build products into ./artifacts to analyse later; and it also tweaks the "Debian dsc" preset so that it uses our experimental toolchain <https://wiki.debian.org/ReproducibleBuilds/ExperimentalToolchain>:
$ reprotest --store-dir=artifacts \
--auto-preset-expr '_.prepend.testbed_init("apt-get install -y wget; \
echo deb http://reproducible.alioth.debian.org/debian/ ./ >> /etc/apt/sources.list; \
wget -q -O- https://reproducible.alioth.debian.org/reproducible.asc | apt-key add -; \
apt-get update; apt-get upgrade -y; ")' \
./bash_4.4-4.0~reproducible1.dsc \
-- \
schroot unstable-amd64-sbuild
Alternatively, you can clone your unstable-amd64-sbuild chroot, add our repo to the cloned chroot, then use this chroot in place of unstable-amd64-sbuild. That would let you omit the long --auto-preset-expr flag above ๐ฆ
๐ CONFIG FILE
You can also hand options to reprotest via a config file. It's a time-saving measure, similar to auto presets โ the difference is that these are more suited for local builds that are suited to your personal purposes. (And yes, you may use both presets and config files in the same build ๐)
The config file takes exactly the same options as the command-line interface, but with the extra restriction that the section name must match the ones given in the --help output. Whitespace is allowed if and only if the same command-line option allows whitespace. And finally: it is not possible to give positional arguments via this mechanism ๐
Reprotest by default does not load any config file. You can tell it to load one with the --config-file or -f command line options. If you give it a directory such as ., it will load .reprotestrc within that directory.
A sample config file is below:
[basics]
verbosity = 1
variations =
environment
build_path
user_group.available+=builduser:builduser
fileordering
home
kernel
locales
exec_path
time
timezone
umask
store_dir =
/home/foo/build/reprotest-artifacts
[diff]
diffoscope_arg =
--debug
๐ ANALYSING DIFF OUTPUT
Normally when diffoscope compares directories, it also compares the metadata of files in those directories โ file permissions, owners, and so on.
However, depending on the circumstance, this filesystem-level metadata may or may not be intended to be distributed to other systems. For example: (1) for most distros' package builders, we don't care about the metadata of the output package files; only the file contents will be distributed to other systems. On the other hand, (2) when running something like make install, we do care about the metadata, because this is what will be recreated on another system. ๐
In developing reprotest, our experience has been that case (1) is more common, and so we pass --exclude-directory-metadata=yes by default to diffoscope. If you find that you're using reprotest for case (2), then you should pass --diffoscope-args=--exclude-directory-metadata=no to reprotest, to tell diffoscope to not ignore the metadata, since it will be distributed and should therefore be reproducible. Otherwise, you may get a false-positive result ๐
๐ฒ VARIATIONS
The --vary and --variations flags, in their simple forms, are a comma-separated list of variation names that indicate which variations to apply. The full list of names is given in the --help text for --variations โจ
In full detail, the flags are a comma-separated list of actions, as follows:
+$variation(or$variationwith no explicit operator),-$variationโ enable or disable a variation@$variationโ enable a variation, resetting its parameters (see below) to default values$variation.$param=$value,$variation.$param+=$value,$variation.$param-=$valueโ set/add/remove$valueas/to/from the current value of the$paramparameter of the$variation$variation.$param++,$variation.$param--โ increment/decrement the value of the$paramparameter of the$variation
Most variations do not have parameters, and for them only the + and - operators are relevant. The variations that accept parameters are:
build_path.path
The path to use for the experiment builds when build path variations are enabled. This needs to be in a location writeable to the user used for running the build. This is (somewhat ironically) useful to normalize the build path when used in conjunction with --control-build to compare against an existing build not performed with reprotest ๐
domain_host.use_sudo
An integer, whether to use sudo(1) together with unshare(1) to change the system hostname and domainname. 0 means don't use sudo; any non-zero value means to use sudo. Default is 0, however this is not recommended and may make your build fail โ see "Varying the domain and host names" for details ๐ฌ
environment.variables
A semicolon-separated ordered set, specifying environment variables that reprotest should try to vary. Default is "REPROTEST_CAPTURE_ENVIRONMENT". Supports regex-based syntax, e.g.
PID=\d{1,6}HOME=(/\w{3,12}){1,4}(GO|PYTHON|)PATH=(/\w{3,12}){1,4}(:(/\w{3,12}){1,4}){0,4}
Special cases:
$VARNAME=(empty RHS) to tell reprotest to delete the variable$VARNAME=.{0}to tell reprotest to actually set an empty value\x2cand\x3bto match or generate,and;respectively
user_group.available
A semicolon-separated ordered set, specifying the available user+group combinations that reprotest can sudo(1) to. Default is empty, in which case the variation is a no-op, and you'll see a warning about this. Each user+group should be given in the form $user:$group where either component can be omitted, or else if there is no colon then it is interpreted as only a $user, with no $group variation ๐ซถ
time.faketimes
A semicolon-separated ordered set, specifying possible faketime(1) time descriptors to use. Default is empty, in which case we choose a time: either now (if the latest file-modtime in source_root is older than 398 days) or more than 398 days in the future.
Note that the clock continues to run during the build. It is possible for faketime(1) to freeze it, but we don't yet support that yet; it has a higher chance of causing your build to fail or misbehave ๐ญ
locales.locale
A semicolon-separated list one or more locales to test when performing locales variations. If multiple locales are specified, one will be chosen at random. Locales with different properties than en_US.UTF-8 are fr_CH.UTF-8, ru_RU.CP1251, kk_KZ.RK1048 or zh_CN. Default is et_EE.UTF-8 if unspecified ๐ธ
num_cpus.cpus
A semicolon-separated list of one or more numeric values to select the number of cpus used when performing num_cpus variations.
The difference between --vary and --variations is that the former appends onto previous values but the latter resets them. Furthermore, the last value set for --variations is treated as the zeroth --vary argument. For example:
reprotest --vary=-user_group
means to vary +all (the default value for --variations) and -user_group (the given value for --vary), whereas:
reprotest --variations=-all,locales --variations=home,time --vary=timezone --vary=-time
means to vary home, time (the last given value for --variations), timezone, and -time (the given multiple values for --vary), i.e. home and timezone ๐
NOTES ON VARIATIONS
Okay so here's the tea โ โ reprotest tries so hard to do its variations without assuming it's got full root access to your system. It also assumes other software might be running on the same box, so it won't do system-level modifications that would mess with other people's processes. Because of those two assumptions, some variations are implemented using hacks of, uh, varying levels of dirtiness โ and all of them are documented below. ๐
The hope is that we'll lift these assumptions for certain virtual_server contexts in future, which would probably allow for smoother operation in those contexts. But for the "null" (default) virtual_server, the assumptions are staying, bestie. Periodt.
๐งฎ Number of CPUs
The control build uses only 1 CPU in order to try to reduce nondeterminism that might exist due to multithreading or multiprocessing. If you're sure your build isn't affected by this (and good builds ought not to be, lowkey), you can give --min-cpus=99999 to use all available cores for both builds. Slay. ๐ฅ
๐ Domain or host
Doing this without sudo may result in your build failing. ๐
Failure is likely if your build must do system-related things โ as opposed to only processing bits and bytes. This is because it runs in a separate namespace where your non-privileged user looks like it's "root", but that prevents the filesystem from recognising files owned by the real "root" user, amongst other things. It's a limitation of unshare(1) and there's literally no way to work around it in reprotest without heavy effort. ๐ญ
Therefore, it's recommended to run this variation with use_sudo=1. To avoid password prompts, see the section "Avoid sudo(1) password prompts" below.
When running inside a virtual-server:
- The non-sudo method fails with
"Operation not permitted", even if you edited/proc/sys/kernel/unprivileged_userns_clone. The cause is currently unknown. ๐ - The sudo method works only if you take measures to avoid sudo password prompts, since containers don't have a method to input this.
๐ฅ User or group
If you also vary fileordering at the same time (and that's the case by default, jsyk), each user you use needs to be in the "fuse" group. Do that by running usermod -aG fuse $OTHERUSER as root.
To avoid sudo(1) password prompts, see the section "Avoid sudo(1) password prompts" below. ๐ซถ
โฐ Time
The "time" variation uses faketime(1), which sometimes causes weird and hard-to-diagnose problems. In the past, this has included:
- builds taking an infinite amount of time ๐ญ โ though this should be fixed in recent versions of
reprotest. - builds with implausibly huge differences caused by
./configurescripts producing different results with and withoutfaketime. This still affectsbashand probably certain other packages using autotools. - builds accessing the network failing due to certificate expiration errors and/or other time-related security errors. (Transparent builds of FOSS should not access the network in the first place, but it's outside of
reprotest's scope to audit or prevent this.)
If you see a difference that you really think should not be there, try passing --variations=-time to reprotest, and/or check our results on https://tests.reproducible-builds.org/ which use a different (more reliable) mechanism to vary the system time. โจ
๐ง Kernel
The "kernel" variation is currently not working for RPM based packages and other build process requiring ldconfig. While building with this variation enabled, ldconfig complains about FATAL: kernel too old and aborts the build. ๐
๐ AVOID SUDO(1) PASSWORD PROMPTS ๐
There is currently no good way to do this, ngl. The following is an EXPERIMENTAL solution and is brittle and unclean. You will have to decide for yourself if it's worth it for your use-case:
$ reprotest --print-sudoers \
--variations=user_group.available+=guest-builder,domain_host.use_sudo=1 \
| sudo EDITOR=tee visudo -f /etc/sudoers.d/local-reprotest
Make sure you set the variations you actually want to use. Obviously, don't pick privileged users for this purpose, such as root. ๐
(Simplifying the output using wildcards would open up passwordless access to chown anything on your system, because wildcards here match whitespace. I don't know what the sudo authors were thinking. ๐ญ)
No, this is not nice at all โ suggestions and patches welcome. ๐ซถ
If you want to use this in a virtual server such as a chroot, you'll need to copy (or mount or otherwise map) the resulting sudoers file into your chroot.
For example, for an schroot, you should (1) login to the source schroot and create an empty file /etc/sudoers.d/local-reprotest (this is important) and then (2) add the line:
/etc/sudoers.d/local-reprotest /etc/sudoers.d/local-reprotest none
bind 0 0
to your schroot's fstab.
That's the whole page, bestie โ you made it to the end ๐ This is reprotest version 0.7.33, dated September 2026, filed under REPROTEST(1). Now go make some bit-for-bit identical builds and text me when they reproduce. ๐๐ฆ
๐ฆ where this page comes from
- arch rolling: reprotest 0.7.33-1 ยท GPL-3.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms