Skip to content

Latest commit

 

History

History
146 lines (95 loc) · 5.71 KB

reproducible-builds.md

File metadata and controls

146 lines (95 loc) · 5.71 KB

Reproducible builds

  1. Creating a reproducible PHAR
    1. PHP-Scoper
    2. Composer
      1. Composer root version
      2. Composer autoload suffix
    3. Box
      1. PHAR alias
      2. Requirement Checker
      3. Box banner
      4. Timestamp
  2. Usages

When building a PHAR, you sometimes want to have reproducible builds, i.e. no matter how many times you build the PHAR, as long as the source content is identical, then the resulting PHAR should not change.

Whilst this sounds like a good idea and easy at first, it is not the default behaviour. Indeed there is a number of things that are generated and will make the resulting PHAR different, for example the Composer autoloader classname, or the scoping prefix if you are using PHP-Scoper.

This documentation aims at walking you through the common elements to adjust in order to achieve reproducible builds. This is not an exhaustive piece of documentation as it will also depends on your own application too.

Creating a reproducible PHAR

PHP-Scoper

If you are using the PHP-Scoper compactor, you will need to define a fixed prefix as otherwise a random one is generated.

See the PHP-Scoper prefix configuration doc.

Composer

Composer root version

By default, the git commit of the current version is included in some places in the Composer generated files. At the time of writing, the current git reference can be found in vendor/composer/installed.{json|php} with the path root.reference.

This is not ideal as the content of the PHAR could be identical for two different git commits. In order to get rid of this problem, you can leverage the COMPOSER_ROOT_VERSION. Either by exporting it or passing it to Box when compiling it:

COMPOSER_ROOT_VERSION=1.0.0-dev box compile

Composer autoload suffix

By default, Box will dump the Composer autoloader which usually results in a different autoloader classname. There is exceptions to this, for example Composer tend to try to keep the known suffix if one already exist, but it is an exotic case that is not recommended to rely on. For this reason you need to configure the Composer autoload prefix:

composer config autoloader-suffix AppChecksum

Or configure it directly in your composer.json:

{
    "config": {
        "autoloader-suffix": "AppChecksum"
    }
}

Box

PHAR Alias

By default, Box generates a random PHAR alias so you need to set a fixed value, e.g. my-app-name.

See the Box alias setting.

The output (string|null) setting specifies the file name and path of the newly built PHAR. If the value of the setting is not an absolute path, the path will be relative to the base path.

If not provided or set to null, the default value used will based on the main. For example if the main file is bin/acme.php or bin/acme then the output will be bin/acme.phar.

Requirement Checker

By default, Box includes its Requirement Checker. It will not change from a PHAR to another, so this step should be skippable. However, the RequirementChecker shipped does change based on the Box version. I.e. building your PHAR with Box 4.3.8 will result in a different† requirement checker shipped than the one in 4.4.0.

†: By different is meant the checksum is different. The behaviour and code may be the exact same. The most likely difference will be the namespace.

Note that this may change in the future: #1075.

Box banner

By default, Box generates a banner. This banners includes the Box version so building the same PHAR with two different Box versions will result in a different PHAR signature.

Timestamp

The files unix timestamp are part of the PHAR signature, hence if they have a different timestamp (which they do as when you add a PHAR to a file, it is changed to the time at when you added it).

To fix this, you can leverage configure the timestamp.

Usages

Deterministic builds are a highly desirable property to prevent targeted malware attacks. They also make it easier to detect if there is any real change. As non-exhaustive examples in the wild: Composer and PHPStan.

Another benefit of such builds is that it makes it easier to know if there was any change. You can know if two PHARs are identical by using the box diff command, or extract the signature out of the box info:signature command:

box info:signature app.phar

And re-use that signature later for comparison. You will loose the ability to do a detailed diff between the two PHARs, but it is enough to know if the PHARs are identical or not.



« Symfony supportPHAR signing best practicies »