Producer: Validate and Release PHP Library Packages

tl;dr: Producer will look over your Composer-based library package just before you are ready to tag it for release, make sure it appears ready-to-go, and then do the release for you through Github, Gitlab, or Bitbucket. Producer works with both Git and Mercurial.

I. History

Back when I was working on Solar, we needed a process to package up each release of the entire framework and make sure I hadn’t forgotten anything. Thanks to the magic of the Internet Archive, you can see it here. You can read more about the 10 year old (!!!) process here; the script is of course PEAR-centric, since PEAR was the main packaging system available in PHP at that time.

After Solar was done, we began extracting its individual components as 30 or so separate packages in Aura. As before, I needed a process to make sure each package release was actually ready-to-go, so that earlier PEAR-centric release script evolved into a collection of Composer-centric commands. These release and management tools are specifically for Aura, with its particular conventions and expectations in mind, and have served well for 3 major versions of the project over the last 5+ years.

But now I have started some non-Aura projects: Relay, Radar, Arbiter, Bookdown, and most recently Atlas. These projects do not have the benefit of the automated release process, with all of its checks and validation, that Aura does.

With that in mind, then, I have extracted a substantial amount of the Aura package release process into a new project, Producer, so that I can use it with any non-Aura library package. That means you can use it with your library package, too.

II. Why Use Producer?

When you think your Git or Mercurial library package repository is ready for a tagged version release, Producer help to validate that it is actually in a high-quality releasable state. Then, if Producer thinks everything looks good, it will release your library package through its remote origin API (i.e., through Github, Gitlab, or Bitbucket).

(Note that Producer is not for regular daily development work. It is specifically for the day you want to release the package. At that time, it can be easy to forget steps in a release process; Producer manages those steps for you.)

III. Validating A Package For Release

Most of the things Producer checks for are there because I forgot to check them myself at some point in the past, and it was embarassing for one reason or another.

For example, you don’t want to make a release from a local copy that has not been updated to match the remote copy, or when the local copy has some modified or uncommitted files. So the very first thing Producer does is to pull down changes from the remote origin, push up local changes to the remote, and then check the local status to see if there are uncommitted files. If the local status check fails, Producer won’t release the package.

After it’s sure the local copy is in a clean state, Producer will run composer validate to make sure it has no obvious errors. Obviously, if composer.json is not valid, the package is not in a releasable state.

Next, Producer looks to see if you have a particular set of non-empty informational files in place. These are administrative, but necessary: README, LICENSE, CONTRIBUTING, and CHANGES (not CHANGELOG yet; more on that later). The files may or may not have .md extensions. These files ought to be present in any packaged release, so Producer will fail if they are not present, or if they are empty.

Of course, Producer can’t tell if their contents are sensible or not, though it can tell if the LICENSE notes the current year. If it does not, Producer will tell you to update the copyright year in the LICENSE. You don’t want to release a package with an outdated copyright year!

After that, Producer will run your test suite with PHPUnit. Producer expects that you have a phpunit.xml.dist file at the root of your package, so if that’s missing, the release will fail. Likewise, if the tests fail, Producer will not release the package.

As part of the test-running, Producer will issue composer update to make sure all the require-dev packages are in place. After the tests pass, Producer will check the local copy status yet again, to make sure the tests have cleaned up after themselves properly. This also has the benefit of checking that your “ignore” files are set up properly; at the very least, composer.lock and vendor/ should be ignored, so their presence in the status check will cause Producer to stop its release process.

We want to have well-documented code, so Producer runs PHPDocumentor to check all the docblocks in the package src/ directory. (Yes, this presumes that you have a src/ directory in your library package.) If any PHP docblocks are missing or malformed, Producer will stop the release. Similarly, if the @package tags are missing or incorrect, Producer will tell you which files and stop the release. (In this case, “correct” means “the same as the Composer package name”.)

As the next-to-last step in the validation process, Producer examines your CHANGES file commit timestamp. If the CHANGES file is not part of the very last commit, Producer will balk, and tell you to update your CHANGES so that all changes have been mentioned in the release notes. (Producer uses CHANGES because that’s what I use in Aura; at some point in the future, this may become CHANGELOG, but for now just the release-specific change listing seems enough.)

Finally, Producer will go to your remote origin API and retrieve a list of open issues for the repository. This is not technically a validation step, only a reminder in case there issues you have forgotten to address. If there are open issues, Producer will not stop the release process.

IV. Releasing A Package

The above validations can be run on their own using the producer validate command; they are useful as a pre-flight or pre-check to the actual release process. When the validate command finishes successfully, you can move on to the release command; the release process is exactly the same as the validation process, with the added step of actually releasing the package when validation passes.

At this point, Producer has already looked at your .git or .hg configuration to determine the remote origin API; this can be Github, Gitlab, or Bitbucket. It then prepares and sends a release through that API, using the CHANGES file for the release notes.

Afterwards, it will sync the local copy with the remote origin to pull down any newly-created tags.

That’s it; you’re done! Producer has now run a series of “final checks” on your repository and released it with a new version number.

V. Questions

When I announced Producer over the weekend, it got posted to Reddit by somone other than me (for once ;-). I hope I have answered the “why is Producer useful?” question with this blog post. Two others remain:

  1. Q: “Why does Producer run composer update as part of validation? It should only look at the current state of the repo, not modify the repo.”

    A: Perhaps “validation” is not the right word to use. It’s intended as a pre-flight or preparatory step towards releasing, to make sure that the package will actually install what it requires through Composer. In addition, for my own projects at least, the tests use the Composer autoloader for the src/ files, so composer update is a necessary preliminary to running the tests.

  2. Q: “Why require PHPDocumentor and PHPUnit as part of Producer? What if I have those already installed somewhere else?”

    A: For myself, global installs of these kinds of ecosystem-level tools seem reasonable, but that may be a function of the fact that I mostly manage library packages; having 30+ installs of PHPUnit and PHPDocumentor is just not to my liking. Having said that, I can see how some folks would have different requirements, so I’ll see if I can modify that requirement in a future version of Producer.

Finally, if you have questions/comments/critique, please raise them as issues over at Github. Thanks, and I hope Producer is as useful to you as its earlier versions in Aura have been to me!

Are you stuck with a legacy PHP application? Subscribe to "Modernizing Legacy Applications in PHP" for tips, tools, and techniques that can help you improve your codebase and your work life!

Share This!Share on Google+Share on FacebookTweet about this on TwitterShare on RedditShare on LinkedIn

9 thoughts on “Producer: Validate and Release PHP Library Packages

  1. I think the step running “composer update” should check first whether the lock file is ignored or no. When managing a project rather than a library, it is a best practice to commit the lock file (the release process might include additional steps like building a phar and attaching it to the release btw)

    • Hmm — while I consider Producer useful mostly for “library” packages, perhaps it could become useful for “project” packages, and in so doing check composer.lock instead of doing an update.

      (/me ponders)

      If you want to take a look at the existing codebase, I’d be interested to see what changes you’d make to enable that.

  2. Composer global installs have a couple of really annoying side effects. I’d far prefer that Producer be buildable as a portable executable (.phar). Box (https://github.com/box-project/box2) is pretty helpful in doing that.

    I just tried building a .phar without success (getting paths correct is a problem) using Box. Came out to about 10Mb. There’s some compaction going on, I think.

    It’d be nice to use Aura.Cli instead of Symfony/Console, too. 😉

    • I understand it’s weaker if you update it to include *only* the current year. But if you update it to include the original year *plus* the current year, a la “Copyright (c) 2011-2016” … ? Which seems reasonable, since the new additions are not copyright from a prior year, but the current year.

  3. Does Producer follows semver when deciding how to version the release? How it choses whether to raise bugifx, minor or major version?

    • Producer does not choose a version number for you (at least not yet); you choose it yourself.

Leave a Reply

Your email address will not be published. Required fields are marked *