Skip to content

Commit

Permalink
doc: Add documentation for reproducible builds (#1077)
Browse files Browse the repository at this point in the history
  • Loading branch information
theofidry authored Oct 15, 2023
1 parent 40d74f1 commit f574a98
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ For the full documentation see https://box-project.github.io/box.
1. [Debugging the scoping](doc/code-isolation.md#debugging-the-scoping)
1. [Docker support](doc/docker.md#docker-support)
1. [Symfony support](doc/symfony.md#symfony-support)
1. [Reproducible builds](doc/reproducible-builds.md#reproducible-builds)
1. [FAQ](doc/faq.md#faq)
1. [Contributing](#contributing)
1. [Upgrade guide](UPGRADE.md#from-27-to-30)
Expand Down
2 changes: 1 addition & 1 deletion doc/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ hands and tweak the `Dockerfile` to your needs.
<br />
<hr />

« [PHAR code isolation](code-isolation.md#phar-code-isolation)[Symfony supports](symfony.md#symfony-support) »
« [PHAR code isolation](code-isolation.md#phar-code-isolation)[Symfony support](symfony.md#symfony-support) »


[docker]: https://www.docker.com/
Expand Down
149 changes: 149 additions & 0 deletions doc/reproducible-builds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Reproducible builds

1. [PHP-Scoper](#php-scoper)
1. [Composer](#composer)
1. [Composer root version](#composer-root-version)
1. [Composer autoload suffix](#composer-autoload-suffix)
1. [Box](#box)
1. [PHAR alias](#phar-alias)
1. [Requirement Checker](#requirement-checker)
1. [Box banner](#box-banner)
1. [PHAR](#phar)


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.

## PHP-Scoper

If you are using the [PHP-Scoper compactor][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][php-scoper-prefix-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`][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-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][box-alias].

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`][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][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: https://github.com/box-project/box/issues/1075.


### Box banner

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


## PHAR

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 [Seldaek PHAR-Utils][phar-utils] with the following script:

```
// resign.php
<?php declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use Seld\PharUtils\Timestamps;
$file = getcwd() . '/' . ($argv[1] ?? '');
if (!is_file($file)) {
echo "File does not exist.\n";
exit(1);
}
$util = new Timestamps($file);
$util->updateTimestamps(new DateTimeImmutable('2017-10-11 08:58:00'));
$util->save($file, Phar::SHA512);
```

Then once your PHAR is built:

```
$ php resign.php app.phar
```

This is obviously not ideal and should be fixed by Box at some point (see https://github.com/box-project/box/issues/1074).


<br />
<hr />

« [Symfony support](symfony.md#symfony-support)[FAQ](faq.md#faq) »


[banner]: ./configuration.md#banner-banner
[box-alias]: ./configuration.md#alias-alias
[composer-autoload-prefix]: https://getcomposer.org/doc/06-config.md#autoloader-suffix
[composer-root-version]: https://getcomposer.org/doc/03-cli.md#composer-root-version
[phar-utils]: https://github.com/Seldaek/phar-utils
[php-scoper-compactor]: ./configuration.md#compactors-compactors
[php-scoper-prefix-doc]: https://github.com/humbug/php-scoper/blob/main/docs/configuration.md#prefix
[requirement-checker]: ./requirement-checker.md
2 changes: 1 addition & 1 deletion doc/symfony.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ this in order to fix the problem.
<br />
<hr />

« [Docker support](docker.md#docker-support)[FAQ](faq.md#faq) »
« [Docker support](docker.md#docker-support)[Reproducible build](reproducible-builds.md#reproducible-builds) »


[composer-autoloader-dump]: configuration.md#dumping-the-composer-autoloader-dump-autoload
Expand Down

0 comments on commit f574a98

Please sign in to comment.