<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Blog entries tagged composer :: mwop.net</title>
  <updated>2015-12-17T09:00:00-06:00</updated>
  <generator uri="https://getlaminas.org" version="2">Laminas_Feed_Writer</generator>
  <link rel="alternate" type="text/html" href="https://mwop.net/blog/tag/composer"/>
  <link rel="self" type="application/atom+xml" href="https://mwop.net/blog/tag/composer/atom.xml"/>
  <id>https://mwop.net/blog/tag/composer</id>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Secure PHAR Automation]]></title>
    <published>2015-12-14T11:45:00-06:00</published>
    <updated>2015-12-17T09:00:00-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2015-12-14-secure-phar-automation.html"/>
    <id>https://mwop.net/blog/2015-12-14-secure-phar-automation.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>For a variety of reasons, I've been working on a utility that is
best distributed via <xhtml:a href="http://php.net/phar">PHAR</xhtml:a> file.
As has been <xhtml:a href="https://archive.is/hDwaA">noted by others</xhtml:a>
(archive.is link, due to lack of availability of original site),
PHAR distribution, while useful, is not without security concerns,
and I decided to investigate how to securely create, distribute,
and update PHAR utilities as part of this exercise.</xhtml:p>
<xhtml:p>This is an account of my journey, as well as concrete steps you
can take to secure your own PHAR downloads.</xhtml:p>
<xhtml:h2>The Roadmap</xhtml:h2>
<xhtml:p>The steps outlined by Pádraic Brady in the afore-linked post
were essentially:</xhtml:p>
<xhtml:ul>
<xhtml:li>Distribute the PHAR over TLS-secured HTTPS.</xhtml:li>
<xhtml:li>Sign your PHAR with a private key.</xhtml:li>
<xhtml:li>Manage self updates securely (i.e., the updates must be over
TLS, and updated PHAR files should be signed using the same private
key).</xhtml:li>
</xhtml:ul>
<xhtml:p>As such, I figured the plan should be:</xhtml:p>
<xhtml:ul>
<xhtml:li>Use <xhtml:a href="https://pages.github.com">GitHub Pages</xhtml:a> for
distribution. This gives me essentially free hosting, and free
TLS.</xhtml:li>
<xhtml:li>Create an OpenSSL key, use it to sign the package, and provide
the public key for download.</xhtml:li>
<xhtml:li>Have functionality built-in to the PHAR for updating and
rolling back.</xhtml:li>
<xhtml:li>Automate creation of the PHAR, as well as pushing it and the
version information to the site.</xhtml:li>
</xhtml:ul>
<xhtml:p>Seems simple enough, right?</xhtml:p>
<xhtml:p>It would have been, had I been able to find examples of each of
the steps. In the end, I spent an afternoon testing different
strategies, and finally came up with what follows.</xhtml:p>
<xhtml:h2>Create an OpenSSL Key</xhtml:h2>
<xhtml:p>The first step is to create an OpenSSL private key. This will be
used to sign the packages.</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ openssl genrsa -des3 -out phar-private.pem 4096
</xhtml:code></xhtml:pre>
<xhtml:p>The above will prompt you for a passphrase, which is used to
encrypt the key.</xhtml:p>
<xhtml:p>For purposes of automation, however, you may not (in fact, will
not, as you'll see later) be able to enter the passphrase. As such,
you'll need to strip it.</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ cp phar-private.pem phar-private.pem.passphrase-protected
$ openssl rsa -<xhtml:span class="hljs-keyword">in</xhtml:span> phar-private.pem -out phar-private-nopassphrase.pem
$ cp phar-private-nopassphrase.pem phar-private.pem
</xhtml:code></xhtml:pre>
<xhtml:p>The second step will prompt you for the passphrase. The
resultant key will have it stripped. You can keep the
passphrase-protected version if you wish; however, it's
functionally equivalent to the new key. If you keep it, place it
somewhere safe.</xhtml:p>
<xhtml:p>From here, create a <xhtml:code>.travis/</xhtml:code> subdirectory in your
project, put the private key in it, and then add that file to your
project <xhtml:code>.gitignore</xhtml:code>:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ mkdir .travis
$ mv phar-private.pem .travis/
$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">".travis/phar-private.pem"</xhtml:span> &gt;&gt; .gitignore
</xhtml:code></xhtml:pre>
<xhtml:p>We're telling git to ignore the key, as we don't want to push it
unencrypted to our repository!</xhtml:p>
<xhtml:h2>Use Box to create the PHAR</xhtml:h2>
<xhtml:p>Now that we have a key, we can think about creating our PHAR
file.</xhtml:p>
<xhtml:p>While PHP provides a <xhtml:a href="http://php.net/phar">ton of
functionality around PHARs</xhtml:a>, the problem is that the manual is
not terribly detailed, and this particular section has often gone
out-of-date. On top of that, to build even a relatively simple PHAR
that has an executable stub takes a ton of knowledge.</xhtml:p>
<xhtml:p>So, let others do the work for you. Specifically, the <xhtml:a href="https://box-project.github.io/box2/">Box Project</xhtml:a>. The team
behind the Box Project has done the hard work for you; all you need
to do is create a configuration file that details what files are
used, what compression to use, what signing mechanism to use (if
any) and where the key is located, etc.</xhtml:p>
<xhtml:p>The link above details retrieving box, but the basics are:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ curl -LSs https://box-project.github.io/box2/installer.php | php
</xhtml:code></xhtml:pre>
<xhtml:p>which will leave an executable <xhtml:code>box.phar</xhtml:code> in the
working directory. I usually put this in my <xhtml:code>$PATH</xhtml:code> and
alias <xhtml:code>box</xhtml:code> to it.</xhtml:p>
<xhtml:p>Box, like many other PHP utilities, allows you to create either
a configuration file, or a "dist" configuration file. I like to do
the latter, as it then lets me copy it locally to provide
modifications/customizations. Here's a basic
<xhtml:code>box.json.dist</xhtml:code> file containing the options for creating
an OpenSSL-signed, gzipped, executable package:</xhtml:p>
<xhtml:pre><xhtml:code class="language-javascript hljs javascript" data-lang="javascript">{
  <xhtml:span class="hljs-string">"algorithm"</xhtml:span>: <xhtml:span class="hljs-string">"OPENSSL"</xhtml:span>,
  <xhtml:span class="hljs-string">"chmod"</xhtml:span>: <xhtml:span class="hljs-string">"0755"</xhtml:span>,
  <xhtml:span class="hljs-string">"compression"</xhtml:span>: <xhtml:span class="hljs-string">"GZ"</xhtml:span>,
  <xhtml:span class="hljs-string">"directories"</xhtml:span>: [
    <xhtml:span class="hljs-string">"src"</xhtml:span>
  ],
  <xhtml:span class="hljs-string">"files"</xhtml:span>: [
    <xhtml:span class="hljs-string">"LICENSE.md"</xhtml:span>
  ],
  <xhtml:span class="hljs-string">"finder"</xhtml:span>: [
    {
      <xhtml:span class="hljs-string">"name"</xhtml:span>: <xhtml:span class="hljs-string">"*.php"</xhtml:span>,
      <xhtml:span class="hljs-string">"exclude"</xhtml:span>: [
        <xhtml:span class="hljs-string">"tests"</xhtml:span>,
        <xhtml:span class="hljs-string">"test"</xhtml:span>
      ],
      <xhtml:span class="hljs-string">"in"</xhtml:span>: <xhtml:span class="hljs-string">"vendor"</xhtml:span>
    }
  ],
  <xhtml:span class="hljs-string">"git-version"</xhtml:span>: <xhtml:span class="hljs-string">"package_version"</xhtml:span>,
  <xhtml:span class="hljs-string">"intercept"</xhtml:span>: <xhtml:span class="hljs-literal">true</xhtml:span>,
  <xhtml:span class="hljs-string">"key"</xhtml:span>: <xhtml:span class="hljs-string">".travis/phar-private.pem"</xhtml:span>,
  <xhtml:span class="hljs-string">"main"</xhtml:span>: <xhtml:span class="hljs-string">"bin/command.php"</xhtml:span>,
  <xhtml:span class="hljs-string">"output"</xhtml:span>: <xhtml:span class="hljs-string">"command.phar"</xhtml:span>,
  <xhtml:span class="hljs-string">"stub"</xhtml:span>: <xhtml:span class="hljs-literal">true</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>Some notes on the various options:</xhtml:p>
<xhtml:ul>
<xhtml:li>"algorithm" and "key" go hand-in-hand. The algorithm indicates
what package signing algorithm to use, and the "key" is the path on
the filesystem to the key; relative paths are relative to the
<xhtml:code>box.json.dist</xhtml:code> file.</xhtml:li>
<xhtml:li>"compression" indicates the compression algorithm to use; in
this case, I used gzip.</xhtml:li>
<xhtml:li>"git-version" is a string to look for in files; technically, it
looks for <xhtml:code>@package_version@</xhtml:code>, and not just the string.
When discovered, it replaces that string with the sha1 of the most
recent commit.</xhtml:li>
<xhtml:li>"directories" is used to specify directories where all files
should be included; "files" is used to specify individual files to
include. Usually I use "files" for anything not in "directories",
like the LICENSE file.</xhtml:li>
<xhtml:li>"finder" can be used in a similar fashion to "directories" and
"files", but gives you the ability to provide filtering rules; it
uses the <xhtml:a href="http://symfony.com/doc/current/components/finder.html">Symfony
Finder component</xhtml:a>, which means that the rules you build will
follow that component's configuration syntax. In the example above,
I'm telling it I want to include the <xhtml:code>vendor/</xhtml:code>
directory, but to only include PHP files, and to exclude files in
the directories "test" or "tests". Doing so greatly reduces the
size of the generated PHAR.</xhtml:li>
<xhtml:li>"main" is the name of the file containing the script to execute
when the PHAR is invoked. Obviously, the name will vary based on
your application.</xhtml:li>
<xhtml:li>"stub", when <xhtml:code>true</xhtml:code>, indicates thatthe default stub
based on the "main" script should be used; you can specify another
stub file as well, if desired. I've used Box several times, and
found that this needs to be boolean <xhtml:code>true</xhtml:code> when I'm
having it encapsulate a command-line script.</xhtml:li>
<xhtml:li>"output" is the name of the file to generate.</xhtml:li>
<xhtml:li>"chmod" indicates the file mode mask to set on the generated
file.</xhtml:li>
</xhtml:ul>
<xhtml:p>Obviously, configure this according to your needs, and add or
remove configuration as befits your project. The <xhtml:a href="https://github.com/box-project/box2/blob/2.0/res/schema.json">schema
file</xhtml:a> is your friend when determining what should be included;
much of the information is also available via <xhtml:code>box.phar help
build</xhtml:code>.</xhtml:p>
<xhtml:p>Once you have created the file, you can attempt a build:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ box build -vv
</xhtml:code></xhtml:pre>
<xhtml:p>(<xhtml:code>-vv</xhtml:code> indicates "verbosity"; I do this so I can see
errors if they occur.)</xhtml:p>
<xhtml:blockquote>
<xhtml:h3>Make the build leaner!</xhtml:h3>
<xhtml:p>The command may take a while. In fact, if you have a lot of
dependencies, it <xhtml:em>will</xhtml:em> take a while. I found it was best to
strip any development-only dependencies prior to running a
build:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ composer install --no-dev
</xhtml:code></xhtml:pre>
<xhtml:p>I find this results in a far faster build, with a much smaller
file size.</xhtml:p>
</xhtml:blockquote>
<xhtml:p>When the build is successful, you'll end up with two files:</xhtml:p>
<xhtml:ul>
<xhtml:li><xhtml:code>command.phar</xhtml:code></xhtml:li>
<xhtml:li><xhtml:code>command.phar.pubkey</xhtml:code></xhtml:li>
</xhtml:ul>
<xhtml:p>The name will be based on what you specified for "output" in the
configuration. The second file is the OpenSSL public key derived
from your private key. Users will need to have <xhtml:em>both</xhtml:em> files
available, as PHP's PHAR functionality will verify the archive
against the public key on every invocation. This is what ensures a
secure distribution!</xhtml:p>
<xhtml:p>Now that you have the files, add them to your
<xhtml:code>.gitignore</xhtml:code>:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">"command.phar"</xhtml:span> &gt;&gt; .gitignore
$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">"command.phar.pubkey"</xhtml:span> &gt;&gt; .gitignore
</xhtml:code></xhtml:pre>
<xhtml:p>We don't want the PHAR in the master branch; this is the branch
used to create the PHAR itself. By excluding it, we can do some
automation later.</xhtml:p>
<xhtml:h2>Generate a version file</xhtml:h2>
<xhtml:p>Now that we have the PHAR, we need to provide a way of
indicating what <xhtml:em>version</xhtml:em> we have. The simplest way to do
that is to take its sha1sum and write it to a file:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ sha1sum command.phar &gt; command.phar.version
</xhtml:code></xhtml:pre>
<xhtml:p>We'll use this later to automate self-updates. Just like the
PHAR file itself and its public key, we'll exclude it from the
branch:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">"command.phar.version"</xhtml:span> &gt;&gt; .gitignore
</xhtml:code></xhtml:pre>
<xhtml:h2>Create the gh-pages branch</xhtml:h2>
<xhtml:p>At this stage, we have a PHAR file that is signed with a private
OpenSSL key, a public OpenSSL key for verifying the signature, and
a version file we can use later for triggering updates. It's time
to publish those.</xhtml:p>
<xhtml:p>As noted above, we want to publish to a TLS-enabled site. GitHub
provides that infrastructure for us via GitHub Pages. These are
available via any public repository that publishes a
<xhtml:code>gh-pages</xhtml:code> branch. The obvious conclusion is: let's add
that branch to our current repository!</xhtml:p>
<xhtml:p>That said, the new branch really shouldn't have the build tools
and code.</xhtml:p>
<xhtml:p>First, let's make sure you've committed everything you need on
the master branch:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git add .gitignore box.json.dist
$ git commit -m <xhtml:span class="hljs-string">'Build tools for PHAR file'</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Now we can create what's called an "orphan" branch — a branch
with no history and no parents:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git checkout --orphan gh-pages
$ git rm -rf .
</xhtml:code></xhtml:pre>
<xhtml:p>We perform the <xhtml:code>git rm -rf .</xhtml:code> command in order to
remove any files previously committed. One nice side effect is that
untracked files — such as our generated PHAR, public key, and
version file — are untouched by this operation!</xhtml:p>
<xhtml:p>So, let's add them:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git add composer.phar*
</xhtml:code></xhtml:pre>
<xhtml:p>It would be good to create a landing page as well, with links
for downloading the PHAR file and the public key (don't worry about
the version file for now). Create an <xhtml:code>index.html</xhtml:code> file
in the root of the project, and add that as well, as well as any
CSS or JavaScript files you need for it. I personally used <xhtml:a href="https://getbootstrap.com">Bootstrap</xhtml:a> from a CDN for this, which
gives a reasonable default look for the package.</xhtml:p>
<xhtml:p>Once all your files are added, execute:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git commit -m <xhtml:span class="hljs-string">'Initial gh-pages files'</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>and then push to your GitHub repository.</xhtml:p>
<xhtml:p>Within a minute or so, you should be able to browse to
<xhtml:code>https://&lt;your username or org&gt;.github.io/&lt;your
repo&gt;/</xhtml:code>.</xhtml:p>
<xhtml:h2>Write self-update/rollback commands</xhtml:h2>
<xhtml:p>Now that we have a process for creating a PHAR, how will users
update or rollback?</xhtml:p>
<xhtml:p>Fortunately, Pádraic has a solution for that as well: <xhtml:a href="https://github.com/padraic/phar-updater">PHAR Updater</xhtml:a>. This
handy library provides functionality for replacing the PHAR, and
has built-in support to verify the signature of the replacement
with the public key. For it to work, the PHAR, public key, and
version file must all be accessible over TLS/SSL.</xhtml:p>
<xhtml:p>First, add the utility to your package:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ composer require padraic/phar-updater
</xhtml:code></xhtml:pre>
<xhtml:p>Regardless of how you write your console commands in PHP, your
self-update command will execute something like the following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-keyword">use</xhtml:span> <xhtml:span class="hljs-title">Humbug</xhtml:span>\<xhtml:span class="hljs-title">SelfUpdate</xhtml:span>\<xhtml:span class="hljs-title">Updater</xhtml:span>;

$updater = <xhtml:span class="hljs-keyword">new</xhtml:span> Updater();
$updater-&gt;getStrategy()-&gt;setPharUrl($urlToGithubPagesPharFile);
$updater-&gt;getStrategy()-&gt;setVersionUrl($urlToGithubPagesVersionFile);
<xhtml:span class="hljs-keyword">try</xhtml:span> {
    $result = $updater-&gt;update();
    <xhtml:span class="hljs-keyword">if</xhtml:span> (! $result) {
        <xhtml:span class="hljs-comment">// No update needed!</xhtml:span>
        <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
    }
    $new = $updater-&gt;getNewVersion();
    $old = $updater-&gt;getOldVersion();
    printf(<xhtml:span class="hljs-string">'Updated from %s to %s'</xhtml:span>, $old, $new);
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
} <xhtml:span class="hljs-keyword">catch</xhtml:span> (\<xhtml:span class="hljs-keyword">Exception</xhtml:span> $e) {
    <xhtml:span class="hljs-comment">// Report an error!</xhtml:span>
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">1</xhtml:span>);
}
</xhtml:code></xhtml:pre>
<xhtml:p>It's really that simple! The defaults assume an OpenSSL-signed
PHAR file, and that the public key is present locally in a file
named <xhtml:code>&lt;name of phar&gt;.pubkey</xhtml:code>. If the version is
different, it updates; if not, it doesn't. Exceptions typically
occur for things like:</xhtml:p>
<xhtml:ul>
<xhtml:li>File permissions.</xhtml:li>
<xhtml:li>Inability to reach the remote PHAR or version files.</xhtml:li>
<xhtml:li>Inability to validate the downloaded PHAR.</xhtml:li>
<xhtml:li>Inability to perform TLS negotation.</xhtml:li>
</xhtml:ul>
<xhtml:p>Regarding this latter, padraic/phar-updater includes
padraic/humbug_get_contents, which is supposed to iron out TLS
issues on PHP versions &lt; 5.6. I found in practice, however, that
when performing the update, if I didn't use a PHP 5.6 version, it
consistently failed, indicating TLS negotiation issues. Supposedly
you can fix these by downloading <xhtml:a href="http://curl.haxx.se/ca/cacert.pem">http://curl.haxx.se/ca/cacert.pem</xhtml:a>
and setting <xhtml:code>openssl.cafile=/path/to/cacert.pem</xhtml:code> in your
<xhtml:code>php.ini</xhtml:code> file.</xhtml:p>
<xhtml:p>So, self-update is taken care of; what about rollback?</xhtml:p>
<xhtml:p>When a self-update is performed using padraic/phar-updater, it
writes the original PHAR to <xhtml:code>command-old.phar</xhtml:code> in the
same directory as the original PHAR file. If that file is
available, you can write a rollback routine like the following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-keyword">use</xhtml:span> <xhtml:span class="hljs-title">Humbug</xhtml:span>\<xhtml:span class="hljs-title">SelfUpdate</xhtml:span>\<xhtml:span class="hljs-title">Updater</xhtml:span>;

$updater = <xhtml:span class="hljs-keyword">new</xhtml:span> Updater();
<xhtml:span class="hljs-keyword">try</xhtml:span> {
    $result = $updater-&gt;rollback();
    <xhtml:span class="hljs-keyword">if</xhtml:span> (! $result) {
        <xhtml:span class="hljs-comment">// report failure!</xhtml:span>
        <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">1</xhtml:span>);
    }
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
} <xhtml:span class="hljs-keyword">catch</xhtml:span> (\<xhtml:span class="hljs-keyword">Exception</xhtml:span> $e) {
    <xhtml:span class="hljs-comment">// Report an error!</xhtml:span>
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">1</xhtml:span>);
}
</xhtml:code></xhtml:pre>
<xhtml:p>Again, quite easy!</xhtml:p>
<xhtml:p>Checkout your project's <xhtml:code>master</xhtml:code> branch, add the
above commands, and re-build your PHAR…</xhtml:p>
<xhtml:p>Wait, shouldn't we automate that last step? It'd be really nice
if we could push to master, and have the new PHAR show up
automatically on our gh-pages branch!</xhtml:p>
<xhtml:h2>Enable Travis-CI for the repository</xhtml:h2>
<xhtml:p>How do we automate? Via continuous integration and deployment,
of course!</xhtml:p>
<xhtml:p>For this step, I'm choosing <xhtml:a href="https://travis-ci.org">Travis-CI</xhtml:a>. It's free for open source
projects, but also has a paid, private tier if you need it. Its
dockerized builds trigger typically within seconds of pushing your
code, and the environment is built and your tests run in often
under 30 seconds. It's a great choice for this.</xhtml:p>
<xhtml:p>As a CI service, it provides a number of stages that trigger,
often conditionally. We're going to use one of these,
<xhtml:code>after_success</xhtml:code>, to build the PHAR, update the version
file, and push them to our gh-pages branch.</xhtml:p>
<xhtml:p>First, though, we need to enable Travis-CI for our repository.
You will need to do the following:</xhtml:p>
<xhtml:ul>
<xhtml:li>Register for a Travis-CI account if you haven't already. The
homepage will guide you there.</xhtml:li>
<xhtml:li>Once you have an account and have logged in, go to your profile
page (clicking your icon in the top right takes you there).</xhtml:li>
<xhtml:li>Find your repository in the list, and toggle the switch to
enable it. (You may need to sync your repositories if your project
is new within the last day; there's typically a "Sync" button next
to where your name appears above the repository list.)</xhtml:li>
</xhtml:ul>
<xhtml:p>From here, you should add a <xhtml:code>.travis.yml</xhtml:code> file to
your project, if you haven't already. Here's a template:</xhtml:p>
<xhtml:pre><xhtml:code class="language-yaml hljs yaml" data-lang="yaml"><xhtml:span class="hljs-attr">sudo:</xhtml:span> <xhtml:span class="hljs-literal">false</xhtml:span>
<xhtml:span class="hljs-attr">language:</xhtml:span> <xhtml:span class="hljs-string">php</xhtml:span>

<xhtml:span class="hljs-attr">cache:</xhtml:span>
  <xhtml:span class="hljs-attr">directories:</xhtml:span>
  <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">$HOME/.composer/cache</xhtml:span>
  <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">vendor</xhtml:span>

<xhtml:span class="hljs-attr">matrix:</xhtml:span>
  <xhtml:span class="hljs-attr">fast_finish:</xhtml:span> <xhtml:span class="hljs-literal">true</xhtml:span>
  <xhtml:span class="hljs-attr">include:</xhtml:span>
  <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-attr">php:</xhtml:span> <xhtml:span class="hljs-number">5.5</xhtml:span>
  <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-attr">php:</xhtml:span> <xhtml:span class="hljs-number">5.6</xhtml:span>
    <xhtml:span class="hljs-attr">env:</xhtml:span>
    <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">EXECUTE_DEPLOYMENT=true</xhtml:span>
  <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-attr">php:</xhtml:span> <xhtml:span class="hljs-number">7</xhtml:span>
  <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-attr">php:</xhtml:span> <xhtml:span class="hljs-string">hhvm</xhtml:span>
  <xhtml:span class="hljs-attr">allow_failures:</xhtml:span>
  <xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-attr">php:</xhtml:span> <xhtml:span class="hljs-string">hhvm</xhtml:span>

<xhtml:span class="hljs-attr">before_install:</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">phpenv</xhtml:span> <xhtml:span class="hljs-string">config-rm</xhtml:span> <xhtml:span class="hljs-string">xdebug.ini</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">composer</xhtml:span> <xhtml:span class="hljs-string">self-update</xhtml:span>

<xhtml:span class="hljs-attr">install:</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">travis_retry</xhtml:span> <xhtml:span class="hljs-string">composer</xhtml:span> <xhtml:span class="hljs-string">install</xhtml:span> <xhtml:span class="hljs-string">--no-interaction</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">composer</xhtml:span> <xhtml:span class="hljs-string">info</xhtml:span> <xhtml:span class="hljs-string">-i</xhtml:span>

<xhtml:span class="hljs-attr">script:</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">./vendor/bin/phpunit</xhtml:span> <xhtml:span class="hljs-comment"># If you have tests</xhtml:span>

<xhtml:span class="hljs-attr">notifications:</xhtml:span>
  <xhtml:span class="hljs-attr">email:</xhtml:span> <xhtml:span class="hljs-literal">true</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Commit and push that file, and you should see your first build
appear on Travis-CI.</xhtml:p>
<xhtml:h2>Create an SSH deploy key</xhtml:h2>
<xhtml:p>If we want Travis-CI to push to our gh-pages branch, we'll need
to provide it with a deployment key.</xhtml:p>
<xhtml:p>First, create a new SSH key:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ ssh-keygen -t rsa -b 4096 -C <xhtml:span class="hljs-string">"&lt;your email address&gt;"</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>This will prompt you for where you want to put the new key
files, and what they should be named; I use descriptive names in
these cases, such as the repository name, and the selected
encryption type: "component_installer_rsa" . Usually
<xhtml:code>$HOME/.ssh/</xhtml:code> is a good location to store them.</xhtml:p>
<xhtml:p>Next, we'll provide the public key to GitHub. Open the public
key generated (usually <xhtml:code>&lt;key name&gt;.pub</xhtml:code> in a
visual editor with clipboard support, and copy the entire file.
Then go to <xhtml:code>https://github.com/&lt;your username or
org&gt;/&lt;your repo&gt;/settings/keys</xhtml:code>. On that page, click
the button <xhtml:code>Add deploy key</xhtml:code>, give your key a name (I
used <xhtml:code>&lt;repo name&gt; for Travis-CI</xhtml:code>), and paste in
the key where indicated. Finally, click the box enabling write
permissions; we want to be able to push commits with this key!
Confirm and save it.</xhtml:p>
<xhtml:p>Finally, we need to copy the <xhtml:em>private</xhtml:em> key into the
project. Don't worry; we're not going to commit it yet; in fact,
we're going to tell git to omit it:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ cp <xhtml:span class="hljs-variable">$HOME</xhtml:span>/.ssh/&lt;repo&gt;_rsa .travis/build-key.pem
$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">".travis/build-key.pem"</xhtml:span> &gt;&gt; .gitignore
</xhtml:code></xhtml:pre>
<xhtml:p>At this point, we now have two files in <xhtml:code>.travis/</xhtml:code>,
neither of which git will commit to the repository:
<xhtml:code>phar-private.pem</xhtml:code> and <xhtml:code>build-key.pem</xhtml:code>. And,
somehow, Travis-CI needs to get access to them.</xhtml:p>
<xhtml:h2>Archive and encrypt the secrets</xhtml:h2>
<xhtml:p>Travis-CI provides a number of facilities for encrypting secrets
that you wish to utilize during the build process. In our case, we
need to provide <xhtml:a href="https://docs.travis-ci.com/user/encrypting-files">encrypted
files</xhtml:a>.</xhtml:p>
<xhtml:p>Interestingly, due to some issues with OpenSSL and the way the
support is implemented in Travis-CI, you can <xhtml:a href="https://github.com/travis-ci/travis.rb/issues/239">only encrypt a
<xhtml:em>single</xhtml:em> file</xhtml:a>. Thus, if you have multiple files, you
need <xhtml:a href="https://docs.travis-ci.com/user/encrypting-files#Encrypting-multiple-files">
create an archive of them and encrypt that</xhtml:a>.</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ <xhtml:span class="hljs-built_in">cd</xhtml:span> .travis
$ tar cvf secrets.tar *.pem
$ <xhtml:span class="hljs-built_in">cd</xhtml:span> ..
</xhtml:code></xhtml:pre>
<xhtml:p>This will create the file <xhtml:code>.travis/secrets.tar</xhtml:code>.</xhtml:p>
<xhtml:p>Now, we need to encrypt the file. To do this, you will need to
install the <xhtml:code>travis</xhtml:code> gem:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ gem install travis
</xhtml:code></xhtml:pre>
<xhtml:p>and then login:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ travis login
</xhtml:code></xhtml:pre>
<xhtml:p>Once you've done that, you can encrypt the
<xhtml:code>secrets.tar</xhtml:code> file:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ travis encrypt-file .travis/secrets.tar .travis/secrets.tar.enc --add
</xhtml:code></xhtml:pre>
<xhtml:p>This will create a new file,
<xhtml:code>.travis/secrets.tar.enc</xhtml:code>, and add an entry to your
<xhtml:code>.travis.yml</xhtml:code>'s <xhtml:code>before_install</xhtml:code> section that
will decrypt the file; this means that your code and scripts on
Travis-CI can then rely on <xhtml:code>.travis/secrets.tar</xhtml:code> being
available.</xhtml:p>
<xhtml:blockquote>
<xhtml:h3>Note for the Type-A personalities out there</xhtml:h3>
<xhtml:p>When you use the <xhtml:code>--add</xhtml:code> flag and <xhtml:code>travis</xhtml:code>
rewrites your <xhtml:code>.travis.yml</xhtml:code> file, it strips out any
whitespace you've added.</xhtml:p>
</xhtml:blockquote>
<xhtml:p>We'll add the <xhtml:code>.travis/secrets.tar.enc</xhtml:code> file to the
repository, and omit <xhtml:code>.travis/secrets.tar</xhtml:code>:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ git add .travis/secrets.tar.enc
$ <xhtml:span class="hljs-built_in">echo</xhtml:span> <xhtml:span class="hljs-string">".travis/secrets.tar"</xhtml:span> &gt;&gt; .gitignore
$ git add .gitignore
</xhtml:code></xhtml:pre>
<xhtml:p>When a build is triggered on Travis-CI now, it will decrypt this
file before any of our build processes are triggered, allowing us
access to those secrets!</xhtml:p>
<xhtml:h2>Write a deployment script</xhtml:h2>
<xhtml:p>Now that we have our secrets securely available on Travis-CI, we
can figure out what deployment might look like:</xhtml:p>
<xhtml:ul>
<xhtml:li>We'll want to remove development-only dependencies.</xhtml:li>
<xhtml:li>We'll want to extract the secrets from the tarball.</xhtml:li>
<xhtml:li>We'll want to start the SSH agent with our deployment key.</xhtml:li>
<xhtml:li>We'll want to setup our Git identity. (In my experiments, I
discovered that GitHub rejected pushes from valid deployment keys
that did not include a full name and email.)</xhtml:li>
<xhtml:li>We'll need to add a git remote using the SSH-enabled repository
path.</xhtml:li>
<xhtml:li>We'll want to fetch the Box Project PHAR file.</xhtml:li>
<xhtml:li>We'll want to create the PHAR using Box.</xhtml:li>
<xhtml:li>We'll need to generate a new version file from the re-generated
PHAR.</xhtml:li>
<xhtml:li>We'll need to check out the gh-pages branch, and add the PHAR
and version file.</xhtml:li>
<xhtml:li>We'll need to push the changes to GitHub.</xhtml:li>
</xhtml:ul>
<xhtml:p>I do all but the first step in a script, which I put in
<xhtml:code>bin/deploy.sh</xhtml:code>:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash"><xhtml:span class="hljs-meta">#!/bin/bash</xhtml:span>
<xhtml:span class="hljs-comment"># Unpack secrets; -C ensures they unpack *in* the .travis directory</xhtml:span>
tar xvf .travis/secrets.tar -C .travis

<xhtml:span class="hljs-comment"># Setup SSH agent:</xhtml:span>
<xhtml:span class="hljs-built_in">eval</xhtml:span> <xhtml:span class="hljs-string">"<xhtml:span class="hljs-variable">$(ssh-agent -s)</xhtml:span>"</xhtml:span> <xhtml:span class="hljs-comment">#start the ssh agent</xhtml:span>
chmod 600 .travis/build-key.pem
ssh-add .travis/build-key.pem

<xhtml:span class="hljs-comment"># Setup git defaults:</xhtml:span>
git config --global user.email <xhtml:span class="hljs-string">"&lt;your email here&gt;"</xhtml:span>
git config --global user.name <xhtml:span class="hljs-string">"&lt;your name here&gt;"</xhtml:span>

<xhtml:span class="hljs-comment"># Add SSH-based remote to GitHub repo:</xhtml:span>
git remote add deploy git@github.com:weierophinney/component-installer.git
git fetch deploy

<xhtml:span class="hljs-comment"># Get box and build PHAR</xhtml:span>
wget https://box-project.github.io/box2/manifest.json
BOX_URL=$(php bin/parse-manifest.php manifest.json)
rm manifest.json
wget -O box.phar <xhtml:span class="hljs-variable">${BOX_URL}</xhtml:span>
chmod 755 box.phar
./box.phar build -vv
<xhtml:span class="hljs-comment"># Without the following step, we cannot checkout the gh-pages branch due to</xhtml:span>
<xhtml:span class="hljs-comment"># file conflicts:</xhtml:span>
mv component-installer.phar component-installer.phar.tmp

<xhtml:span class="hljs-comment"># Checkout gh-pages and add PHAR file and version:</xhtml:span>
git checkout -b gh-pages deploy/gh-pages
mv component-installer.phar.tmp component-installer.phar
sha1sum component-installer.phar &gt; component-installer.phar.version
git add component-installer.phar component-installer.phar.version

<xhtml:span class="hljs-comment"># Commit and push:</xhtml:span>
git commit -m <xhtml:span class="hljs-string">'Rebuilt phar'</xhtml:span>
git push deploy gh-pages:gh-pages
</xhtml:code></xhtml:pre>
<xhtml:p>You'll note that this script makes reference to
<xhtml:code>bin/parse-manifest.php</xhtml:code>; this is a PHP script that
parses the version manifest file for Box to find the download URL
of the latest <xhtml:code>box.phar</xhtml:code>. It looks like this:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-meta">&lt;?php</xhtml:span>
chdir(<xhtml:span class="hljs-keyword">__DIR__</xhtml:span> . <xhtml:span class="hljs-string">'/../'</xhtml:span>);
$fallbackUrl = <xhtml:span class="hljs-string">'https://github.com/box-project/box2/releases/download/2.6.0/box-2.6.0.phar'</xhtml:span>;

<xhtml:span class="hljs-keyword">if</xhtml:span> (! <xhtml:span class="hljs-keyword">isset</xhtml:span>($argv[<xhtml:span class="hljs-number">1</xhtml:span>]) || ! is_file($argv[<xhtml:span class="hljs-number">1</xhtml:span>])) {
    <xhtml:span class="hljs-keyword">return</xhtml:span> $fallbackUrl;
}

$manifestJson = file_get_contents($argv[<xhtml:span class="hljs-number">1</xhtml:span>]);
$files = json_decode($manifestJson, <xhtml:span class="hljs-keyword">true</xhtml:span>);

<xhtml:span class="hljs-keyword">if</xhtml:span> (! is_array($files)) {
    <xhtml:span class="hljs-keyword">echo</xhtml:span> $fallbackUrl;
    <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
}

<xhtml:span class="hljs-keyword">foreach</xhtml:span> ($files <xhtml:span class="hljs-keyword">as</xhtml:span> $file) {
    <xhtml:span class="hljs-keyword">if</xhtml:span> (! is_array($file) || ! <xhtml:span class="hljs-keyword">isset</xhtml:span>($file[<xhtml:span class="hljs-string">'version'</xhtml:span>])) {
        <xhtml:span class="hljs-keyword">continue</xhtml:span>;
    }

    <xhtml:span class="hljs-keyword">if</xhtml:span> (version_compare($file[<xhtml:span class="hljs-string">'version'</xhtml:span>], <xhtml:span class="hljs-string">'2.6.0'</xhtml:span>, <xhtml:span class="hljs-string">'&gt;='</xhtml:span>)) {
        <xhtml:span class="hljs-keyword">echo</xhtml:span> $file[<xhtml:span class="hljs-string">'url'</xhtml:span>];
        <xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
    }
}

<xhtml:span class="hljs-keyword">echo</xhtml:span> $fallbackUrl;
<xhtml:span class="hljs-keyword">exit</xhtml:span>(<xhtml:span class="hljs-number">0</xhtml:span>);
</xhtml:code></xhtml:pre>
<xhtml:p>Essentially, it attempts to parse the manifest, and, on any
failure, uses a known-good URI. You could, of course, just use the
known-good URI always.</xhtml:p>
<xhtml:blockquote>
<xhtml:h3>Why not use the Box installer?</xhtml:h3>
<xhtml:p>I'm trying to demonstrate a secure toolchain. As <xhtml:a href="https://archive.is/hDwaA">Pádraic outlines in his post</xhtml:a>,
installer scripts can introduce remote code execution
vulnerabilities, particularly if directly piped to the PHP
executable. In this particular case, the Box
<xhtml:code>installer.php</xhtml:code> is using an insecure URI for downloading
the <xhtml:code>manifest.json</xhtml:code> (I've proposed <xhtml:a href="https://github.com/box-project/box2/pull/122">a patch</xhtml:a> for
this). As such, I'm downloading the manifest over SSL, manually
parsing it, and then downloading the PHAR file from the parsed
results.</xhtml:p>
</xhtml:blockquote>
<xhtml:p>Make the deployment script executable, add both scripts to your
repository, and commit!</xhtml:p>
<xhtml:h2>Add the script to travis</xhtml:h2>
<xhtml:p>Now we need to tell Travis-CI to execute this script. We want it
to run:</xhtml:p>
<xhtml:ul>
<xhtml:li>Only for one build environment. (No need to push multiple times
for the same commit!)</xhtml:li>
<xhtml:li>Only if the build is successful.</xhtml:li>
<xhtml:li>Only for builds on the master branch.</xhtml:li>
<xhtml:li>Only if the build is not for a pull request.</xhtml:li>
</xhtml:ul>
<xhtml:p>If you used the <xhtml:code>.travis.yml</xhtml:code> file I provided earlier
as a template, you likely noted the section where I define the env
variable <xhtml:code>$EXECUTE_DEPLOYMENT</xhtml:code>. This is what enforces
the first point (only run for one build environment).</xhtml:p>
<xhtml:p>For the second point, we're going to define an
<xhtml:code>after_success</xhtml:code> section in the configuration; this
ensures it does not trigger if our other tasks fail (such as unit
tests, CS checks, etc.).</xhtml:p>
<xhtml:p>For the third and fourth points, Travis-CI provides some
environment variables to help us:</xhtml:p>
<xhtml:ul>
<xhtml:li><xhtml:code>$TRAVIS_BRANCH</xhtml:code> indicates the branch. However, in
the case of a pull request, this will be the base branch against
which the pull request was made. As such, we also need:</xhtml:li>
<xhtml:li><xhtml:code>$TRAVIS_PULL_REQUEST</xhtml:code>. The value of this is the
pull request ID when present; otherwise, it's the string
"false".</xhtml:li>
</xhtml:ul>
<xhtml:p>Putting it all together results in the following additions to
the <xhtml:code>.travis.yml</xhtml:code> file:</xhtml:p>
<xhtml:pre><xhtml:code class="language-yaml hljs yaml" data-lang="yaml"><xhtml:span class="hljs-attr">after_success:</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">if</xhtml:span> <xhtml:span class="hljs-string">[[</xhtml:span> <xhtml:span class="hljs-string">$EXECUTE_DEPLOYMENT</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'true'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_BRANCH</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'master'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_PULL_REQUEST</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'false'</xhtml:span> <xhtml:span class="hljs-string">]];</xhtml:span> <xhtml:span class="hljs-string">then</xhtml:span> <xhtml:span class="hljs-string">composer</xhtml:span> <xhtml:span class="hljs-string">install</xhtml:span> <xhtml:span class="hljs-string">--no-dev</xhtml:span> <xhtml:span class="hljs-string">;</xhtml:span> <xhtml:span class="hljs-string">fi</xhtml:span>
<xhtml:span class="hljs-bullet">-</xhtml:span> <xhtml:span class="hljs-string">if</xhtml:span> <xhtml:span class="hljs-string">[[</xhtml:span> <xhtml:span class="hljs-string">$EXECUTE_DEPLOYMENT</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'true'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_BRANCH</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'master'</xhtml:span> <xhtml:span class="hljs-string">&amp;&amp;</xhtml:span> <xhtml:span class="hljs-string">$TRAVIS_PULL_REQUEST</xhtml:span> <xhtml:span class="hljs-string">==</xhtml:span> <xhtml:span class="hljs-string">'false'</xhtml:span> <xhtml:span class="hljs-string">]];</xhtml:span> <xhtml:span class="hljs-string">then</xhtml:span> <xhtml:span class="hljs-string">./bin/deploy.sh</xhtml:span> <xhtml:span class="hljs-string">;</xhtml:span> <xhtml:span class="hljs-string">fi</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Each line only executes if we are on the designated environment
(we chose 5.6 for this example), on the "master" branch, for
non-pull-request pushes. The first line removes the development
dependencies from the tree, and the second executes our deployment
script.</xhtml:p>
<xhtml:blockquote>
<xhtml:h3>Note on after_success vs deploy</xhtml:h3>
<xhtml:p>Travis-CI has another event, <xhtml:code>deploy</xhtml:code>, which is often
touted as the appropriate place to perform, well, deployments,
which is essentially what we're doing in the above.</xhtml:p>
<xhtml:p>What I found, however, is that the workflow didn't work well
when you have cached or encrypted files.</xhtml:p>
<xhtml:p>The deploy event, when triggered, stashes changes, does a clean
checkout, and then tries to restore from the stash. What I observed
was that my cached composer files (the <xhtml:code>composer.lock</xhtml:code>
file and <xhtml:code>vendor/</xhtml:code> directory) created conflicts when
applying the stash, which caused my deployment script to never
trigger. Another time I observed that the decrypted version of my
secrets disappeared with the new checkout.</xhtml:p>
<xhtml:p>If any readers have any feedback on this, I'd love to hear
it!</xhtml:p>
</xhtml:blockquote>
<xhtml:h2>Push and watch it work</xhtml:h2>
<xhtml:p>Hopefully, if you've been following along this far, you'll see
that on your next push with a successful build, your gh-pages
branch will get a new commit, with an updated PHAR and version
file!</xhtml:p>
<xhtml:p>The workflow for consumers will then be:</xhtml:p>
<xhtml:ul>
<xhtml:li>Go to your gh-pages site and download the PHAR file and public
key.</xhtml:li>
<xhtml:li>Periodically execute <xhtml:code>&lt;name of phar file&gt;.phar
self-update</xhtml:code> to update their installation (assuming you named
the self-update command "self-update").</xhtml:li>
<xhtml:li>If desired, they can later rollback to a previous version using
<xhtml:code>&lt;name of phar file&gt;.phar rolback</xhtml:code> (assuming you
named the rollback command "rollback").</xhtml:li>
</xhtml:ul>
<xhtml:p>All of this can be done securely, because you've setup a secure
workflow:</xhtml:p>
<xhtml:ul>
<xhtml:li>The PHAR file, public key, and version file are all secured via
TLS.</xhtml:li>
<xhtml:li>The PHAR file is signed using an OpenSSL private key, and can
be verified using its public complement.</xhtml:li>
</xhtml:ul>
<xhtml:p>In your own workflow, you're only pushing <xhtml:em>encrypted</xhtml:em>
secrets to the repository, and the keys for those are known only to
you and Travis-CI. (In fact, you only "know" them through the
<xhtml:code>travis</xhtml:code> gem!) If your deployment key is compromised,
you can revoke it from GitHub. If you feel the signing key has been
compromised, you can create a new one, and notify your users that
they need to re-download the PHAR and public key.</xhtml:p>
<xhtml:h2>Still under research</xhtml:h2>
<xhtml:p>While the above workflow is tested and works, I have one item
I'm still unhappy with: I'd really like it if the deployment could
be delayed until I know <xhtml:em>all</xhtml:em> environments have completed
successfully. If anybody could assist me with that, I'd love to
hear from you!</xhtml:p>
<xhtml:h2>Updates</xhtml:h2>
<xhtml:p>Below is a list of updates made to this post since the time of
writing:</xhtml:p>
<xhtml:ul>
<xhtml:li>2015-12-15: Changed references to <xhtml:code>composer update</xhtml:code>
to read <xhtml:code>composer install</xhtml:code>, per a comment from
Christophe Coevoet.</xhtml:li>
<xhtml:li>2015-12-15: Changed OpenSSL key generation example to use 4096
bits instead of 2048, per a comment from sf_tristanb.</xhtml:li>
<xhtml:li>2015-12-17: Updated the <xhtml:code>bin/deploy.sh</xhtml:code> script to
download the <xhtml:code>box.phar</xhtml:code> securely, instead of using their
installer script.</xhtml:li>
</xhtml:ul>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2015-12-14-secure-phar-automation.html">Secure
PHAR Automation</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2015-12-14T11:45:00-06:00">14 December
2015</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[OpenShift, ZF2, and Composer]]></title>
    <published>2012-11-01T15:25:00-05:00</published>
    <updated>2012-11-01T15:25:00-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2012-11-01-openshift-zf2-composer.html"/>
    <id>https://mwop.net/blog/2012-11-01-openshift-zf2-composer.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I was recently shopping around for inexpensive cloud hosting; I
want to try out a couple of ideas that may or may not have much
traffic, but which aren't suited for my VPS setup (the excellent
<xhtml:a href="http://servergrove.com/">ServerGrove</xhtml:a>); additionally,
I'm unsure how long I will maintain these projects. My budget for
this is quite small as a result; I'm already paying for hosting,
and am quite happy with it, so this is really for experimental
stuff.</xhtml:p>
<xhtml:p>I considered Amazon, Orchestra.io, and a few others, but was
concerned about the idea of a ~$50/month cost for something I'm
uncertain about.</xhtml:p>
<xhtml:p>When I asked in <xhtml:a href="irc://irc.freenode.net/zftalk.dev">#zftalk.dev</xhtml:a>, someone
suggested <xhtml:a href="http://openshift.redhat.com/">OpenShift</xhtml:a> as
an idea, and coincidentally, the very next day <xhtml:a href="http://www.zend.com/en/company/news/press/379_red-hat-expands-openshift-ecosystem-with-zend-partnership-to-offer-professional-grade-environment-for-php-developers">
Zend announced a partnership with RedHat surrounding OpenShift</xhtml:a>.
The stars were in alignment.</xhtml:p>
<xhtml:p>In the past month, in the few spare moments I've had (which
included an excellent OpenShift hackathon at ZendCon), I've created
a quick application that I've deployed and tested in OpenShift.
These are my findings.</xhtml:p>
<xhtml:h2>ZF2</xhtml:h2>
<xhtml:p>I didn't really have to do anything different to have <xhtml:a href="http://framework.zend.com/">zf2</xhtml:a> work; the standard
<xhtml:code>.htaccess</xhtml:code> provided in the skeleton application worked
flawlessly the first time (I've worked with some cloud environments
where this is not the case).</xhtml:p>
<xhtml:p>The only frustration I had was the default directory structure
OpenShift foists upon us:</xhtml:p>
<xhtml:pre><xhtml:code class="language-powershell hljs powershell" data-lang="powershell"><xhtml:span class="hljs-keyword">data</xhtml:span>/
libs/
misc/
php/
</xhtml:code></xhtml:pre>
<xhtml:p>This is not terrible, by any stretch. However, it's attempting
to dictate the application structure, which I'm not terribly happy
with — particularly as my structure may vary based on the framework
I'm using (or not!), and because I may already have a project
written that I simply want to deploy.</xhtml:p>
<xhtml:p>In particular, the <xhtml:code>php</xhtml:code> directory is galling — it's
simply the document root. Most frameworks I've used or seen call
the equivalent directory <xhtml:code>public</xhtml:code>, or <xhtml:code>web</xhtml:code>,
or <xhtml:code>html</xhtml:code> — but never <xhtml:code>php</xhtml:code> (in large part
because the only PHP file under the document root in most
frameworks is the <xhtml:code>index.php</xhtml:code> that acts as the front
controller). It would be nice if this were configurable.</xhtml:p>
<xhtml:p>This conflicts a bit with how a ZF2 app is structured. I ended
up doing the following:</xhtml:p>
<xhtml:ul>
<xhtml:li>Removed <xhtml:code>php</xhtml:code> and symlinked my <xhtml:code>public</xhtml:code>
directory to it.</xhtml:li>
<xhtml:li>Removed <xhtml:code>libs</xhtml:code> and symlinked my <xhtml:code>vendor</xhtml:code>
directory to it.</xhtml:li>
<xhtml:li>Removed <xhtml:code>misc</xhtml:code> as I had no need to it.</xhtml:li>
</xhtml:ul>
<xhtml:p>Nothing too big, thankfully — but problematic from the
perspective of, "I've already developed this app, but now I have to
make changes for it to work on a specific cloud vendor."</xhtml:p>
<xhtml:h2>Composer</xhtml:h2>
<xhtml:p>My next question was how to use <xhtml:a href="http://getcomposer.org/">Composer</xhtml:a> during my deployment
process, and some some googling <xhtml:a href="https://openshift.redhat.com/community/content/support-for-git-clone-on-the-server-aka-support-php-composerphar">
found some answers for me</xhtml:a>.</xhtml:p>
<xhtml:p>Basically, I needed to create a <xhtml:code>deploy</xhtml:code> task that
does two things:</xhtml:p>
<xhtml:ul>
<xhtml:li>Unset the <xhtml:code>GIT_DIR</xhtml:code> environment variable. Evidently,
the build process operates as part of a git hook, and since
Composer often uses git repositories, this can lead to
problems.</xhtml:li>
<xhtml:li>Change directory to <xhtml:code>OPENSHIFT_REPO_DIR</xhtml:code>, which is
where the application root (not document root!) lives.</xhtml:li>
</xhtml:ul>
<xhtml:p>Once I did those, I could run my normal composer installation.
The <xhtml:code>deploy</xhtml:code> task looks like this:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash"><xhtml:span class="hljs-meta">#!/bin/bash</xhtml:span>
<xhtml:span class="hljs-comment"># .openshift/action_hooks/deploy</xhtml:span>
( <xhtml:span class="hljs-built_in">unset</xhtml:span> GIT_DIR ; <xhtml:span class="hljs-built_in">cd</xhtml:span> <xhtml:span class="hljs-variable">$OPENSHIFT_REPO_DIR</xhtml:span> ; /usr/<xhtml:span class="hljs-built_in">local</xhtml:span>/zend/bin/php composer.phar install )
</xhtml:code></xhtml:pre>
<xhtml:p>This leads into my next topic.</xhtml:p>
<xhtml:h2>Deployment</xhtml:h2>
<xhtml:p>First off, as you probably guessed from that last secton, there
<xhtml:strong>are</xhtml:strong> hooks for deployment — it doesn't have to be
simply git. I like this, as I may have additional things I want to
do during deployment, such as retrieving and installing
site-specific configuration files, installing Composer-defined
dependencies (as already noted), etc.</xhtml:p>
<xhtml:p>Over all, this is pretty seamless, but it's not without issues.
I've been told that some of my issues are being worked on, so those
I won't bring up here. The ones that were a bit strange, and which
caught me by surprise, though, were:</xhtml:p>
<xhtml:ul>
<xhtml:li>Though the build process creates the site build from git, your
<xhtml:strong>submodules are not updated recursively</xhtml:strong>. This
tripped me up, as I was using <xhtml:a href="https://github.com/EvanDotPro/EdpMarkdown">EdpMarkdown</xhtml:a>, and
had installed it as a submodule. I ended up having to import it,
and its own submodule, directly into my project so that it would
work.</xhtml:li>
<xhtml:li>I installed the <xhtml:a href="http://www.mongodb.org/">MongoDB</xhtml:a>
cartridge. Ironically, it was not then enabled in Zend Server, and
I had to go do this. This should be turnkey.</xhtml:li>
<xhtml:li><xhtml:code>/usr/bin/php</xhtml:code> is not the same as
<xhtml:code>/usr/local/zend/bin/php</xhtml:code>. This makes no sense to me if
I've installed Zend Server as my base gear. Considering they're
different versions, this can be hugely misleading and lead to
errors. I understand there are reasons to have both — so simply be
aware that if you use the Zend Server gear, your tasks likely
should use <xhtml:code>/usr/local/zend/bin/php</xhtml:code>.</xhtml:li>
</xhtml:ul>
<xhtml:h2>The good parts?</xhtml:h2>
<xhtml:ul>
<xhtml:li><xhtml:a href="https://openshift.redhat.com/community/faq/i-have-deployed-my-app-but-i-don%E2%80%99t-like-telling-people-to-visit-myapp-myusernamerhcloudcom-how-c">
You can alias an application to a DNS CNAME</xhtml:a> — meaning you can
point your domain name to your OpenShift applications.
Awesome!</xhtml:li>
<xhtml:li>Simplicity of adding capabilities, such as Mongo, MySQL, Cron,
and others. In most cases, this is simply a "click on the button"
and it's installed and available.</xhtml:li>
<xhtml:li><xhtml:a href="http://www.zend.com/en/products/server">Zend
Server</xhtml:a>. For most PHP extensions, I can turn them on or off with
a few mouse clicks. If I want page-level caching, I don't have to
do anything to my application; I can simply setup some rules in the
Zend Server interface and get on with it, and enjoy tremendous
boosts to performance. I used to enjoy taming and tuning servers;
most days anymore, I just want them to work.</xhtml:li>
<xhtml:li><xhtml:a href="https://openshift.redhat.com/community/developers/remote-access">SSH</xhtml:a>
access to the server, with a number of commands to which I've been
given <xhtml:code>sudoer</xhtml:code> access. If you're going to sandbox
somebody, this is a fantastic way to do it. Oh, also: SSH tunnels
to services like Mongo and MySQL just work (via the
<xhtml:code>rhc-port-forward</xhtml:code> command).</xhtml:li>
</xhtml:ul>
<xhtml:h2>Summary</xhtml:h2>
<xhtml:p>Over all, I'm quite pleased. While it took me a bit to find the
various incantations I needed, the service is quite flexible. For
my needs, considering I'm doing experimental stuff, the price can't
be beat (the current developer preview is free). Considering most
stuff I do will fall into this or the basic tier, and that most
cartridges do not end up counting against your alotment of gears,
the pricing ($0.05/hour) is extremely competitive.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2012-11-01-openshift-zf2-composer.html">OpenShift,
ZF2, and Composer</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2012-11-01T15:25:00-05:00">1 November
2012</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
</feed>
