This package is a powerful and flexible automated deployment tool for PHP
projects. It is best suited for, but by no means limited to, deploying TYPO3,
Flow and Neos applications. It is inspired by some features of Capistrano
(thank you) in terms of the Git workflow.
Surf package is a complete automated deployment tool. It is best used but by far
not limited to deploy TYPO3 CMS and Flow applications. It's inspired by some
features of Capistrano (thanks) concerning the Git workflow.
Some features of Surf:
Remote checkout of Git repositories with submodules
Flexible, declarative configuration of deployments
This way, you can add /usr/local/surf to PHP Include Paths in your IDE.
Upgrading Surf
Later, to upgrade Surf, run the command:
surf self-update
Copied!
Global composer installation
To install Surf globally via composer, run the following command:
composer globalrequire typo3/surf:^3.6
Copied!
This way, you can add ~/.composer/vendor/typo3/surf to PHP Include Paths in your IDE.
Local composer installation
To install Surf within your project via composer, run the following command:
composer require typo3/surf:^3.6
Copied!
The advantage of this method is that you can directly inspect the source files of surf without any further configuration in your IDE.
Note
Because of surf's dependencies it could be that you can't install surf in your project root. In that case you should install surf in a sub directory.
Building a Surf phar from source
Surf is built using humbug/box and the process is simple:
Install humbug/box as described in its documentation
Clone or download the desired branch of typo3/surf
cd your/surf/folder
composer install --no-dev
path/to/box compile
The generated surf.phar in the folder release should work as expected.
Usage
After installing Surf you have to create one or more deployment configuration files for your project.
The deployment configuration files are at the moment just plain php files.
So you can do what ever you can dream of what is possible with php itself.
We recommend to keep the deployment configuration as simple as possible and do it in the first place in a procedural like style.
Per default Surf expects the deployment configuration files within the .surf directory in your project.
If you like you can specify the configuration directory with the command option --configurationPath (Rollback deployment) or you could add an environment variable called SURF_WORKSPACE.
But for now we are going to place our deployment configuration files in the .surf directory.
We start by creating a simple deployment configuration in ~/.surf/MyDeployment.php for a deployment
with name MyDeployment:
<?php
$node = new \TYPO3\Surf\Domain\Model\Node('example');
$node->setHostname('example.com');
$node->setDeploymentPath('/home/my-flow-app/app');
$node->setOption('username', 'myuser');
$application = new \TYPO3\Surf\Application\Neos\Flow();
$application->setVersion('4.0');
$application->setOption('repositoryUrl', 'git@github.com:myuser/my-flow-app.git');
$application->addNode($node);
$deployment->addApplication($application);
Copied!
That's a very basic deployment based on the default Flow application template TYPO3\Surf\Application\Neos\Flow.
The deployment object is available to the script as the variable $deployment. A node is basically a deployment
target representing a server for an application. The node is assigned to the applications for the deployment. Finally
the application is added to the deployment. Depending on the usecase, the deployment path can also be set in the application
object which is then set for all nodes. This is especially useful if you have several applications which you want to deploy
to the same node for example a backend and frontend application.
Each application resembles a repository with code. So a more complex deployment could both deploy a Flow application
and release an extension for a TYPO3 CMS website. Also different roles can be expressed using applications, since every
task can be registered to run for all or a specific application instance.
In this basic deployment above are a lot of sensitive defaults configured behind the curtains.
You are going to explore them in the different chapters of the documentation.
SSH Authentication Types
The preferred way of connecting to the remote host is via SSH Public-Key authentication.
That's why in the example above, only the username and hostname are set.
However, due to constraints in the infrastructure setup, sometimes, deployment
scenarios do not work with public key authentication. Surf also supports
password-based SSH authentication. For that, you need to specify the password
as follows:
Authentication with passwords needs the expect unix tool which is installed
by default in most Linux distributions.
Custom Connection
In case you need to connect to the remote host via more esoteric protocols, you can
also implement your own remote host connection: In this case, set the option
remoteCommandExecutionHandler on the node:
$node->setOption('remoteCommandExecutionHandler', function(\TYPO3\Surf\Domain\Service\ShellCommandService $shellCommandService, $command, Node $node, Deployment $deployment, $logOutput = TRUE){
// Now, do what you need to do in order to connect to $node and execute $command.// You can call $shellCommandService->executeProcess() here.// This function should return a two-element array where the first array element// is an integer containing the Exit Code, and the second array element is a// string with the full, trimmed, output.
});
Copied!
Test a deployment
You can get a description of the deployment by running:
$ surf describe MyDeployment
Copied!
Simulate the deployment by running:
$ surf simulate MyDeployment
Copied!
The simulation gives a hint which tasks will be executed on which node. During simulation no harmful tasks will be
executed for real. If a remote SSH command would be executed it will be printed in the log messages starting with
... $nodeName: "command".
But be aware, if the simulation is working fine it does not mean everything is working correctly for the real deployment.
Sorry for that. We are working on it to test really everything during the simulation. But this task is difficult, we tell you.
Run a deployment
If everything looks right, you can run the deployment:
$ surf deploy MyDeployment
Copied!
To include extra details in the output, you can increase verbosity with the --verbose option:
-v for normal output,
-vv for more verbose output,
-vvv for debug.
Surf is going to create the following directories on the deployment host:
releases contains releases dirs,
shared contains shared files and dirs,
releases/next (temporarily)
releases/current symlink to current release
releases/previous (optional)
Configure your hosts to serve your public directory from current.
By default Surf keeps the all the releases, but you can configure this number by modifying the associated option:
$application->setOption('keepReleases', 2);
Copied!
Customization
Using git for deployment
By default Surf uses rsync and composer for deployment. But you can also use git, by adding the following configuration
to your Application:
Using rsync can speed up your deployment and doesn't require composer and git on the production server.
Architecture
In order to better understand the concept of the Surf deployment process we will explain some fundamental terminology in this section.
Basically you should grasp the following four main terms to understand the basic concept of the deployment process:
Workflow
The workflow defines the execution process of the deployment and consists of a number of stages and tasks for every stage.
Surf ships already with one concrete Workflow called SimpleWorkflow.
But you can define your own workflow as long as your workflow class inherits from the \TYPO3\Surf\Domain\Model\Workflow class shipped with Surf.
The application is the code you want to ship. The deployment configuration has at least one application.
But you can configure as many applications as you want in one deployment configuration.
The application itself contains of one or more nodes.
You can also define within the workflow whether a task is to apply to all applications or only to a specific application.
If your deployment configuration is not overriding the workflow option, then you are using the default SimpleWorkflow class shipped with Surf.
The SimpleWorkflow class defines 9 stages of the deployment process which are sequentially called.
Each stage can consists of none, one or multiple tasks running one after another.
You can add your own tasks for each stage. If you like, you can also specify if your custom task is running before or after a task already defined for this stage.
In the list below you can see all the 9 steps defined by the SimpleWorkflow:
initialize
This is normally used only for an initial deployment to an instance. At this stage you may prefill certain directories for example.
Example Task: \TYPO3\Surf\Task\CreateDirectoriesTask
package
This stage is where you normally package all files and assets, which will be transferred to the next stage.
Example Task: \TYPO3\Surf\Task\Package\GitTask
transfer
Here all tasks are located which serve to transfer the assets from your local computer to the node, where the application runs.
Example Task: \TYPO3\Surf\Task\Transfer\RsyncTask
update
If necessary, the transferred assets can be updated at this stage on the foreign instance.
Example Task: \TYPO3\Surf\Task\TYPO3\CMS\SymlinkDataTask
migrate
Here you can define tasks to do some database updates / migrations. Be careful and do not delete old tables or columns, because the old code, relying on these, is still live.
Example Task: \TYPO3\Surf\Task\TYPO3\CMS\SetUpExtensionsTask
finalize
This stage is meant for tasks, that should be done short before going live, like cache warm ups and so on.
Example Task: \TYPO3\Surf\Task\Neos\Flow\PublishResourcesTask
test
In the test stage you can make tests, to check if everything is fine before switching the releases.
Example Task: \TYPO3\Surf\Task\Test\HttpTestTask
switch
This is the crucial stage. Here the old live instance is switched with the new prepared instance. Normally the new instance is symlinked.
Example Task: \TYPO3\Surf\Task\SymlinkReleaseTask
cleanup
At this stage you would cleanup old releases or remove other unused stuff.
Example Task: \TYPO3\Surf\Task\CleanupReleasesTask
You can create your own workflow if you like. In order to do so you have to extend the abstract Workflow class.
The creation of a custom workflow is out of the scope of this chapter. Have a look at the SimpleWorkflow in oder to do so.
Note
But we recommend to just manipulate the stages provided by the SimpleWorkflow in order to customize your deployment flow.
Manipulate the flow
If you like to add your own tasks to a specific stage of the flow, you can just add them the following ways:
// Add tasks to a specific stage
$workflow->addTask('YourTask', 'cleanup');
// Add tasks that shall be executed after the given stage
$workflow->afterStage('YourTask', 'cleanup');
// Add tasks that shall be executed before the given stage
$workflow->beforeStage('YourTask', 'cleanup');
// Add tasks that shall be executed before the given task
$workflow->beforeTask(AnotherTask::class, 'YourTask');
// Add tasks that shall be executed after the given task
$workflow->afterTask(AnotherTask::class, 'YourTask');
Copied!
If you like to remove certain tasks from the flow, just do it like that:
// You remove the given task from every application
$workflow->removeTask(FlushCachesTask::class);
// Only remove the task for a specific application
$workflow->removeTask(FlushCachesTask::class, $application);
Copied!
Applications
The application is the code you want to ship. The deployment configuration has at least one application.
But you can configure as many applications as you want in one deployment configuration.
The application itself contains of one or more nodes.
You can also define within the workflow whether a task is to apply to all applications or only to a specific application.
Surf already ships with specific applications with a sensitive default configuration for the execution process (which tasks are to be called in which stage).
For example there is one application class for TYPO3 or Neos shipped with Surf.
But you can create your own specific Application class as long as it inherits from \TYPO3\Surf\Domain\Model\Application.
By default (as long as the application inherits from \\TYPO3\\Surf\\Application\\BaseApplication) an application uses rsync and composer as transfer and package method. But you can also use git, by adding the following configuration to your application:
A tag name or tag glob pattern that is understood by git ls-remote.
Surf uses git (ls-remote --sort=version) to sort the results versiony and return the highest matching tag.
Example::
$releaseChannel = 'live';
$application
// this would checkout the commit with highest tag matching "live-*",// for example live-1.2.3
->setOption('tag', $releaseChannel . '-*')))
// ...
Copied!
Special Use Cases
Applying Cherry-Picks to Git Repositories: Post-Checkout commands
When you want to execute some commands directly after checkout, such as cherry-picking not-yet-committed bugfixes, you can set the gitPostCheckoutCommands option on the application, being a two-dimensional array.
The key contains the path where the command shall execute, and the value is another array containing the commands themselves.
A node is basically a deployment target representing a server for an application. The node is assigned to an application for the deployment.
A simple node configuration looks like this:
<?php
$node = new \TYPO3\Surf\Domain\Model\Node('example');
$node->setHostname('example.com');
$node->setOption('username', 'myuser');
$application->addNode($node);
Copied!
SSH Authentication Types
The preferred way of connecting to the remote host is via SSH Public-Key authentication. That's why in the example above, only the username and hostname are set.
However, due to constraints in the infrastructure setup, sometimes, deployment scenarios do not work with public key authentication. Surf also supports password-based SSH authentication. For that, you need to specify the password as follows:
In case you need to connect to the remote host via more esoteric protocols, you can also implement your own remote host connection: In this case, set the option remoteCommandExecutionHandler on the node:
<?php
$node->setOption('remoteCommandExecutionHandler', function(ShellCommandService $shellCommandService, $command, Node $node, Deployment $deployment, $logOutput = TRUE){
// Now, do what you need to do in order to connect to $node and execute $command.// You can call $shellCommandService->executeProcess() here.// This function should return a two-element array where the first array element// is an integer containing the Exit Code, and the second array element is a// string with the full, trimmed, output.
});
SSH provides a way to reuse an existing SSH connection for subsequent connection attempts to the same host.
Add something like this to /.ssh/config to reuse existing SSH connections:
Host myhost.uberspace.de
ControlMaster auto
ControlPath /tmp/ssh_mux_%h_%p_%r
ControlPersist 600
Copied!
Tasks
Since a deployment configuration is just a plain PHP file you can create custom tasks by creating for example a NpmInstallTask class which itself must extend in the whole inheritance chain the abstract class \\TYPO3\\Surf\\Domain\\Model\\Task shipped with Surf:
In this case we create a task to install the npm dependencies locally.
The example shows some simple things to be aware of.
First of all you see the string {workspacePath}. This placeholder gets replaced by the full workspace path for the current application.
We will see later that for the remote shell tasks, we have more of these placeholders (Placeholders).
Secondly we see that the task defines a default command if none is specified. If you like you can override this option in your deployment configuration.
This is valid for all the delivered tasks shipped with Surf.
For this simple task above, we recommend to simplify this by just using the possibility to define your task dynamically by the following mechanism provided by Surf:
This way you create a task dynamically by extending the base task \TYPO3\Surf\Task\LocalShellTask::class with an array of options. In this case the command option is mandatory for the LocalShellTask.
We will show you another convenient and often used way to customize the deployment workflow with your own tasks:
In this case we create a task dynamically based on the ShellTask. The ShellTask execute one or more provided commands on the target machine.
As you can see, we have used some other placeholders compared to the LocalShellTask above.
For the remote ShellTask the following placeholders are available:
Placeholders
workspacePath: The path to the local workspace directory
deploymentPath: The path to the deployment base directory
releasePath: The path to the release directory in work (typically referenced by next)
sharedPath: The path to the shared directory for all releases
currentPath: The path that points to the current release
previousPath: The path that points to the previous release
Add task to the deployment flow
So we have seen how to create custom tasks in different ways. In the following we will see how we add these tasks to the deployment flow:
This will execute the new task after the stage transfer only for the application referenced by $application.
Besides specifying the execution point via a stage, you can also give an existing task as an anchor and specify the task execution with afterTask or beforeTask:
The following table shows all the methods to manipulate the tasks in the deployment flow (part of the abstract Workflow class):
Method
Arguments
Description
defineTask
$taskName, $taskType, ($options)
Defines a new task with name $taskName based on $taskType with custom options.
addTask
$tasks, $stage, ($application)
Add one or more tasks to the workflow that should run in the given stage.
removeTask
$taskName
Removes the task with the given name from all stages and applications.
afterTask
$taskName, $tasks, ($application)
Adds one or more tasks that should run after the given task name.
beforeTask
$taskName, $tasks, ($application)
Adds one or more tasks that should run before the given task name.
Options for Task
In order to customize options of existing tasks you can do it the following ways:
<?phpuseTYPO3\Surf\Task\Transfer\RsyncTask;
// Customize the option for the task only for a specific application
$application->setOption(RsyncTask::class . '[rsyncExcludes]', [
'.git',
'public/fileadmin',
]);
// Customize the option for the task only for a specific node
$node->setOption(RsyncTask::class . '[rsyncExcludes]', [
'public/fileadmin',
]);
// Customize the option for the whole deployment
$deployment->setOption(RsyncTask::class . '[rsyncExcludes]', [
'.git',
]);
Copied!
The order is important because application options override node options and node options override deployment options.
CLI Usage
After installation of Surf you will have the ability to run the surf command from your terminal.
Surf will by default check for your deployment configurations in the subfolder .surf.
To get list of all available tasks run the surf command:
TYPO3 Surf [version]
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose Increase the verbosity of messages: 1for normal output, 2for more verbose output and3for debug
Available commands:
deploy Deploys the given name
describe Describes the flow for the given name
help Displays help for a command
list Lists commands
migrate Migrates old deployment definitions to new Surf version
rollback Rollback current to previous release and remove current folder
show Shows all the deployments depending on the directory configuration
simulate Simulates the deployment for the given name
Copied!
List available deployments
You can get a list of available deployments by running:
$ surf show
Copied!
Test a deployment
You can get a description of the deployment by running:
$ surf describe MyDeployment
Copied!
Simulate the deployment by running:
$ surf simulate MyDeployment
Copied!
The simulation gives a hint which tasks will be executed on which node. During simulation no harmful tasks will be
executed for real. If a remote SSH command would be executed it will be printed in the log messages starting with
... $nodeName: "command".
Run a deployment
If everything looks right, you can run the deployment:
$ surf deploy MyDeployment
Copied!
Rollback deployment
If your release does not meet your demand, you can quickly rollback to the previous one:
$ surf rollback MyDeployment
Copied!
Using a different configuration path
If you want to use a different configuration path than .surf use the provided option like this:
$ surf show --configurationPath myConfigurationPath
Copied!
Smoke Testing
As you do automated deployments, you should check if the website is up and running
before switching it to the live site. This is called a Smoke Test. We will give
an example for using the built-in HTTP smoke test.
First, you need to create a virtual host with document root in "<deploymentDirectory>/releases/next/Web".
While a deployment is running, the new website will be available under this URL and can
be used for testing.
Then, add a test as follows to the deployment configuration:
remote: if TRUE, the smoke test is triggered through the SSH channel on the remote host
via command-line CURL. If false, it is triggered from the deploying host.
expectedStatus: expected HTTP status code
expectedHeaders: HTTP Header Strings which are expected (can be a multiline string,
each header being on a separate line)
expectedRegexp: Regular Expression to test the contents of the HTTP response against
Further options:
timeout (only if remote=FALSE): HTTP timeout to use
port (only if remote=FALSE): HTTP Port to use
method (only if remote=FALSE): HTTP method to use (default GET)
username (only if remote=FALSE): HTTP Authentication username
password (only if remote=FALSE): HTTP Authentication Password
data (only if remote=FALSE): HTTP payload
proxy (only if remote=FALSE): HTTP Proxy to use
proxyPort (only if remote=FALSE): HTTP Proxy port to use
additionalCurlParameters (only if remote=TRUE): list of parameters which
is directly passed to CURL. Especially useful to e.g. disable SSL certificate
check (with --insecure)
Tests in test stage and caches
In the test stage, the caches of the application is not flushed in order not to affect the live page.
A possible solution is to disable caches when running smoke tests in the test stage on "next" release.
TYPO3
AdditionalConfiguration.php:
if ($context->isTesting()){
foreach ($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] as $cacheName => $cacheConfiguration) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'][$cacheName]['backend'] = \TYPO3\CMS\Core\Cache\Backend\NullBackend::class;
}
}
If you are not able to set environment variables via .htaccess, you can use composer autoloading and a PHP file.
Thanks to the team of jweiland.net for this solution.
<?php// Set the application context in this file because it is not possible to set environment variables// via .htaccess e.g. on domainFACTORY/jweiland.net servers
$context = 'Production';
// detect application context by domainif (array_key_exists('HTTP_HOST', $_SERVER)) {
if (0 === strpos($_SERVER['HTTP_HOST'], 'next.')) {
$context = 'Testing';
}
}
putenv('TYPO3_CONTEXT=' . $context);
The Flow version used in a project can be set using:
<?php/** @var \TYPO3\Surf\Domain\Model\Deployment $deployment */
$application = new \TYPO3\Surf\Application\Neos\Flow();
$application->setVersion('4.0');
Copied!
It defaults to 4.0, so if you are using an older Flow version, you need to set the version as x.y.
This switches Surf behavior to call Flow commands correctly.
If the configuration of a Flow application should be different depending on the deployment configuration
(e.g. database settings or external services) the TYPO3\Surf\Task\Neos\Flow\CopyConfigurationTask task can be used to override
configuration after the code update (Git checkout).
If a Configuration folder exists inside a folder named after your deployment ~/.surf/deployments/MyDeployment
every file in there will be copied to the release Configuration folder recursively.
How to deploy Laravel applications
If you would like to deploy a Laravel application a good starting point is to use Laravel Application class provided by Surf:
Move deployment scripts form Build/Surf to ~/.surf/deployments
Rename task or use migrate command to switch to new task names
Set transferMethod and packageMethod options in your application,
as the default changed from git to rsync
Change options for CreateDirectoriesTask: Now the specified directories are based on the application's release
path not the general deployment path (which did not make much sense)
Neos CMS only: Add the task TYPO3\Surf\Task\Neos\Neos\ImportSiteTask to the step migrate again
if you use the Neos Application and need it to import your content after each deployment
Sitemap
Index
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.