Packaging a TYPO3 Extension with Composer

So you've developed a TYPO3 extension and want to publish it in the TYPO3 Extension Repository (TER). But first you have to create a zip file that can be uploaded to TER. As a reminder: The zip file has to be named after the extension name and the version number, e.g. myextension_1.2.3.zip.

Goal: Executing a one-liner that creates a zip file from the current project under git with the extension name and current version number set in the zip's file name automatically.

But first let's have a look at the methods that are suggested on the TER upload form.

Short disclaimer: As I'm a Linux guy, the examples are tested with Linux. But they should work on other UNIX-like operating systems as well.

TL;DR: If you just want to know the result, jump to the "Easy Packaging" chapter at the bottom of the page.

1. Using the zip Command

zip -r ../my_extension_key_x.y.z.zip *

With this command I have to think about the extension key (my_extension_key) and the current version number (x.y.z), especially if I release more than one extension at a time.

Okay, you can optimise the command a little bit:

zip -r ../${PWD##*/}_x.y.z.zip *

The ${PWD##*/} takes the last part of the current directory path (PWD = print working directory). Now you don't have to know the extension name (as long as the parent directory holds the extension name).

But it has one big disadvantage: I have to manually remove the files and folders I don't want to ship with the extension, like a test or documentation folder or a changelog file. These files and folders have no place on a production server! So I have to clean up first, manually or by script. I don't like that.

2. Using the git archive Command

git archive -o "${PWD##*/}_x.y.z.zip" x.y.z

The git archive command is nicer than the zip command because it takes the .gitattributes file into account. In this file you can (and should) define the files and folders that are ignored by installing the extension with composer. Better. But I also have to think about the version number.

Using the Linux Tools

So I came up with that idea:

  1. Extract the version number from ext_emconf.php
  2. Hand it over to the git archive command

Extract the Version Number

Let's start with extracting the version number. That sounds like a job for grep with regular expressions. To call to mind: The version string in ext_emconf.php looks like 'version' => '1.2.3'. After fiddling around a little, I came to this solution:

grep -Po "(?<='version' => ')([0-9]+\.[0-9]+\.[0-9]+)" ext_emconf.php
(out)1.2.3

Great. The version number was found correctly. But what does this command in detail? Well:

With the -P option grep is instructed to interpret the given pattern as a Perl-compatible regular expression (PCRE).

The -o option means, that only the matched parts of a matching line are returned. Without this option, the entire line will be returned.

Now it gets a bit challenging with the regular expression. The (?<='version' => ') part looks for the version array string as defined in the file ext_emconf.php: 'version' => '. But we don't need the complete line as result, so we use the positive lookbehind function of PCRE.

The pattern ([0-9]+\.[0-9]+\.[0-9]+) identifies the version number. It consists of three numbers separated with dots.

The last part of the command is the file name we want to search the version number in, ext_emconf.php.

Great! This achievement was unlocked. But how do we get the result of grep into the git archive command?

Call git archive with Version Number

Linux has a command for converting standard input into arguments of a command: xargs. For better readability the line-continuation backslash is used to split up the commands into two lines.

grep -Po "(?<='version' => ')([0-9]+\.[0-9]+\.[0-9]+)" ext_emconf.php \
    | xargs -I {version} git archive -o "${PWD##*/}_{version}.zip" {version}

The -I option instructs the command to replace the occurrences of {version} with the standard input argument (which is the output of the grep command). Then we substitute x.y.z from the original example with the {version} variable.

Some Adjustments

I've made some adjustments to meet my workflow:

  • Version tags are prefixed with a v.
  • Add the option -v to the git archive command to list the added files. This can be helpful if you forget to add a file or folder to .gitattributes. You will see it when reviewing the list of files.
  • The zip file is stored in the project's root directory. This pollutes the project's workspace. So I'm collecting the zip files in an another folder, named zip on the same level as the projects's folder.
  • And last but not least, if this zip folder doesn't exist, create it.

That leads us to the following one-liner:

grep -Po "(?<='version' => ')([0-9]+\.[0-9]+\.[0-9]+)" ext_emconf.php \
    | xargs -I {version} sh -c 'mkdir -p ../zip; git archive -v -o "../zip/${PWD##*/}_{version}.zip" v{version}'

The advantage is now: I don't have to know the extension name (as long as the project folder is named after the extension) and the current version number. It just works!

Easy Packaging

But wait! How should I remember that command? Well, I can create an alias or a shell script. But hey, we have a composer.json file which is capable of calling scripts. So let's put the one-liner in our extension's composer.json file:

{
    "scripts": {
        "zip": "grep -Po \"(?<='version' => ')([0-9]+\\.[0-9]+\\.[0-9]+)\" ext_emconf.php | xargs -I {version} sh -c 'mkdir -p ../zip; git archive -v -o \"../zip/${PWD##*/}_{version}.zip\" v{version}'"
    }
}
composer.json

After tagging a new release, a new version of an extension can be packaged with:

composer zip

And is now ready for uploading to TER.