[WIP] This draft aims to document how to maintain Composer based packages within the Debian PHP PEAR (and Composer) Maintainers <pkg-php-pear@lists.alioth.debian.org> team.

Document initated by taffit, documenting his usual (opinionated) workflow, that is aimed to be validated and improved by the team. Please do document alternative workflows to help discussing what we, as a team, want to use as a base.

Index

Packaging

Note: Composer based packages are handled with pkg-php-tools(7), and do not use composer(1) (that is also maintained within the team the following documented way). Instead, they provide a homemade autoload.php file in an expected location (usually /usr/share/php/<Vendor_name>/<Project_name>/autoload.php) in order to load the library and its dependencies (also packaged in Debian instead of being vendored).

Naming policy

Upstream Composer packages are named (as can be seen in the name field from their composer.json file) as <vendor_name>/<project_name>. The usual Debian package name should be php-<vendor_name>-<project_name> to be automatically discovered by our tooling. Diversion from this naming scheme should be documented and installed as /usr/share/pkg-php-tools/overrides/<binary_package_name> with the following syntax.

Also, most source packages build only one binary package, so both name should match (<binary_package_name> and <source_package_name> should be the same). Exception exists (feel free to look at php-laravel-framework or symfony for example, but you may really wish to begin with more simple packages).

<vendor_name> <project_name> <binary_package_name>

Repository

Packages are maintained within the team namespace on salsa. If you don’t yet have write access, please ask on the mailing list (most existing developers are also group admin). Feel free to pick examples among the latest uploaded packages for the latest workflow improvements. Even PHP libraries related to specific packaging efforts (e.g., phpmyadmin, spip, etc.) should be maintained within the team in order to help process unification.

Initial repository

Repository can be setup via the web interface but also via command line (once salsa(1) is properly set up).

salsa create_repo --group php-team/pear <source_package_name>

If you’re starting from scratch, you may want to setup your initial local repository and link it to salsa.d.o.

mkdir <source_package_name>
cd <source_package_name>
git init
git remote add origin git@salsa.debian.org:php-team/pear/<source_package_name>.git

Upstream handling

Because most Composer based library (usually maintained upstream on ?GitHub) are distributed without their testsuite, we often base Debian packages directly from the upstream Git repository. gbp-buildpackage(1) workflow is used as a base, amended for our specific needs.

From the <source_package_name> directory, you may setup the upstream repository as a remote.

git remote add upstream https://github.com/<vendor_name>/<project_name>
git fetch --all

Of course, this is not a perfect world, so sometimes the upstream URL doesn’t match exactly this document scheme, please adapt.

salsa-ci

Salsa CI can be setup via the web interface but also via command line (once salsa(1) is properly set up).

salsa update_safe --group php-team/pear --ci-config-path recipes/debian.yml@salsa-ci-team/pipeline <source_package_name>

debian/ directory structure

When starting from scratch, it may help to start by copying the debian/ directory from a similar package (same <vendor_name>, same license, same kind of package…).

debian/gbp.conf

In order to follow DEP-14, gbp(1) can be set up to use the proper debian-branch and upstream-branch. pristine-tar(1) also helps to reproduce the upstream tarball. Finally, upstream-vcs-tag is used to integrate the upstream repository in the Debian Git repository. Most upstream use v1.2.3 kind of tags, some (e.g. phpunit) use directly 1.2.3 kind of tags, in that case, upstream-vcs-tag = %(version%~%-)s (without v) should be used instead.

[DEFAULT]
debian-branch = debian/latest
filter = [ '.gitattributes' ]
pristine-tar = True
upstream-branch = upstream/latest
upstream-vcs-tag = v%(version%~%-)s

debian/watch

uversionmangle allows to handle pre-version (rc, alpha, beta, etc.) the Debian usual way. pgpmode=gittag allows to check upstream signed tags (in that case, debian/upstream/signing-key.asc should contain the public key) and may be removed if the upstream tags are not signed (you may ask upstream to sign tags).

version=4
options=uversionmangle=s/-?([^\d.]+)/~$1/;tr/A-Z/a-z/,\
mode=git,gitmode=full,gitexport=all,pgpmode=gittag \
https://github.com/<vendor_name>/<project_name> \
refs/tags/v?(\d.+)

Once debian/control (especially the proper Source field) and debian/changelog (with a low enough upstream version) is documented, one should be able to import the upstream package.

gbp import-orig --uscan

debian/control

Usually looks like the following.

Source: php-<vendor_name>-<project_name>
Section: php
Priority: optional
Maintainer: Debian PHP PEAR Maintainers <pkg-php-pear@lists.alioth.debian.org>
Uploaders: <name and email address>
Build-Depends: debhelper-compat (= <latest one, e.g. ''13'' at the time of writing>),
               dh-sequence-phpcomposer,
               phpab,
               phpunit,
               <other build dependency, including dependencies needed to run the testsuite>
Standards-Version: <latest one, e.g. ''4.6.1'' at the time of writing>
Vcs-Git: https://salsa.debian.org/php-team/pear/php-<vendor_name>-<project_name>.git
Vcs-Browser: https://salsa.debian.org/php-team/pear/php-<vendor_name>-<project_name>
Homepage: <real homepage if available, https://github.com/<vendor_name>/<project_name> otherwise>
Rules-Requires-Root: no

Package: php-<vendor_name>-<project_name>
Architecture: all
Depends: ${misc:Depends}, ${phpcomposer:Debian-require}
Suggests: ${phpcomposer:Debian-suggest}
Replaces: ${phpcomposer:Debian-replace}
Breaks: ${phpcomposer:Debian-conflict}, ${phpcomposer:Debian-replace}
Provides: ${phpcomposer:Debian-provide}
Description: ${phpcomposer:description}
 <Long description>

debian/install

That needs to be adapted depending in the upstream repository layout and should match the library namespace documented in the autoload field of composer.json. Often (almost usually), the library namespace matches <Vendor_name>/<Project_name> (something like ucfirst(<vendor_name>)/ucfirst(<project_name>)). The package should also install upstream files in /usr/share/php/<Vendor_name>/<Project_name>.

If the library namespace doesn’t match this format (i.e., if the documented library namespace in the autoload field of composer.json is different of <Vendor_name>/<Project_name>, with <vendor_name>/<project_name> as name in composer.json), it should be documented and installed as /usr/share/pkg-php-tools/autoloaders/<binary_package_name> with the following syntax. This allows phpabtpl(1) to provide a proper phpab(1) template.

<vendor_name> <project_name> <library namespace>

debian/install examples

Library namespace matching the content of lib/, as Doctrine packages (e.g., php-doctrine-common).

lib/* usr/share/php

$ grep -A4 \"autoload\" composer.json 
    "autoload": {
        "psr-4": {
            "Doctrine\\Common\\": "lib/Doctrine/Common"
        }
    },

Library namespace dumped inside src/, as PHPUnit packages (e.g., phpunit-type).

src/* usr/share/php/SebastianBergmann/Type

$ grep -A4 \"autoload\" composer.json
    "autoload": {
        "classmap": [
            "src/"
        ]
    },
$ head src/*.php | tail -n1
namespace SebastianBergmann\Type;

debian/rules

Example for a library namespace dumped inside src/. Some adjustments will be needed, looking at other packages source will help finding the proper syntax.

   1 #!/usr/bin/make -f
   2 
   3 %:
   4         dh $@
   5 
   6 execute_before_dh_auto_build:
   7 
   8         # Build a template for phpab(1)
   9         # (not needed if the package has no dependency)
  10         phpabtpl \
  11                 --basedir src \
  12                 composer.json \
  13                 > debian/autoload.php.tpl
  14 
  15         # Build a static classloader, shipped with the package
  16         phpab --output src/autoload.php \
  17                 # Following line only needed if a template was built
  18                 --template debian/autoload.php.tpl src
  19 
  20 execute_before_dh_auto_test:
  21         # Build classloader for tests
  22         mkdir --parents vendor <Vendor_name>
  23         phpabtpl \
  24                 --require <vendor_name>/<project_name> \
  25                 # If packages from ''require-dev'' in ''composer.json''
  26                 # are actually needed to run the testsuite
  27                 --require phpspec/prophecy-phpunit \
  28                 # If ''files'' from ''autoload-dev'' in ''composer.json''
  29                 --require-file ../<path to file> \
  30                 > debian/autoload.tests.php.tpl
  31         phpab \
  32                 --output vendor/autoload.php \
  33                 --template debian/autoload.tests.php.tpl
  34                 tests
  35         # Workaround to ensure the local class takes precedence during tests.
  36         ln -sr src <Vendor_name>/<Project_name>
  37 
  38 override_dh_auto_test:
  39         phpunit

debian/tests/control

Mimicking commands from the end of override_dh_auto_build as well as override_dh_auto_test in debian/rules is usually enough. Simplified example follows.

Test-Command: mkdir -p vendor && phpabtpl --require <vendor_name>/<project_name> > debian/autoload.tests.php.tpl && phpab -o vendor/autoload.php -t debian/autoload.tests.php.tpl tests && phpunit
Restrictions: rw-build-tree, allow-stderr
Depends: phpab, phpunit, pkg-php-tools, @

debian/clean

Some files or directories produced during the build needs to be cleaned up, examples follow.

.phpunit.cache/
.phpunit.result.cache
debian/autoload.php.tpl
debian/autoload.tests.php.tpl
src/autoload.php
<Vendor_name>/
vendor/

debian/copyright

Short example follows.

Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: <sometimes <vendor_name>/<project_name>>
Upstream-Contact: <often the main copyright holder>
Source: https://github.com/<vendor_name>/<project_name>

Files: *
Copyright: <upstream copyright holder>
License: <short name>
 <dump or link to the documented one in /usr/share/common-licenses/>

debian/README.source

In case gbp import-orig --uscan is not enough to import new upstream version, it should be documented here.

Building workflow

[TODO]

Tricks

gitk(1), gitg(1) or tig(1) are useful to have a visual understanding of the repository structure.

lintian-brush(1) is helpful for the usual maintenance.

lintian-brush --no-update-changelog --uncertain --allow-reformatting --verbose

wrap-and-sort(1) helps in keeping the files uniformed (making further changes more visible in git commits).

Rooms for improvement in our tooling

Clean up automatically usual files and directories instead of manually listing them in debian/clean.