public function push($files, $localRevision = null)
{
if (empty($localRevision)) {
// We will write this in the server
$localRevision = $this->currentRevision();
}
$initialBranch = $this->currentBranch();
// If revision is not HEAD, the current one, it means this is a rollback.
// So, we have to revert the files the the state they were in that revision.
if ($this->revision != 'HEAD') {
$this->cli->out(' Rolling back working copy');
// BUG: This does NOT work correctly for submodules & subsubmodules (and leaves them in an incorrect state)
// It technically should do a submodule update in the parent, not a checkout inside the submodule
$this->git->command('checkout ' . $this->revision, $this->repo);
}
$filesToDelete = $files['delete'];
// Add deleted directories to the list of files to delete. Git does not handle this.
$dirsToDelete = [];
if (count($filesToDelete) > 0) {
$dirsToDelete = $this->hasDeletedDirectories($filesToDelete);
}
$filesToUpload = $files['upload'];
unset($files);
// No longer needed
// Upload Files
if (count($filesToUpload) > 0) {
foreach ($filesToUpload as $fileNo => $file) {
if ($this->currentSubmoduleName) {
$file = $this->currentSubmoduleName . '/' . $file;
}
// Make sure the folder exists in the FTP server.
$dir = explode('/', dirname($file));
$path = '';
$ret = true;
// Skip mkdir if dir is basedir
if ($dir[0] !== '.') {
// Loop through each folder in the path /a/b/c/d.txt to ensure that it exists
// @TODO Can be improved by using: $filesystem->write('path/to/file.txt', 'contents');
for ($i = 0, $count = count($dir); $i < $count; ++$i) {
$path .= $dir[$i] . '/';
if (!isset($pathsThatExist[$path])) {
if (!$this->connection->has($path)) {
$this->connection->createDir($path);
$this->cli->out(" + Created directory '{$path}'.");
$pathsThatExist[$path] = true;
} else {
$pathsThatExist[$path] = true;
}
}
}
}
$filePath = $this->repo . '/' . ($this->currentSubmoduleName ? str_replace($this->currentSubmoduleName . '/', '', $file) : $file);
$data = @file_get_contents($filePath);
// It can happen the path is wrong, especially with included files.
if ($data === false) {
$this->cli->error(' ! File not found - please check path: ' . $filePath);
continue;
}
$remoteFile = $file;
$uploaded = $this->connection->put($remoteFile, $data);
if (!$uploaded) {
$this->cli->error(" ! Failed to upload {$file}.");
} else {
$this->deploymentSize += filesize($this->repo . '/' . ($this->currentSubmoduleName ? str_replace($this->currentSubmoduleName . '/', '', $file) : $file));
}
$numberOfFilesToUpdate = count($filesToUpload);
$fileNo = str_pad(++$fileNo, strlen($numberOfFilesToUpdate), ' ', STR_PAD_LEFT);
$this->cli->lightGreen(" ^ {$fileNo} of {$numberOfFilesToUpdate} <white>{$file}");
}
}
// Delete files
if (count($filesToDelete) > 0) {
foreach ($filesToDelete as $fileNo => $file) {
if ($this->currentSubmoduleName) {
$file = $this->currentSubmoduleName . '/' . $file;
}
$numberOfFilesToDelete = count($filesToDelete);
$fileNo = str_pad(++$fileNo, strlen($numberOfFilesToDelete), ' ', STR_PAD_LEFT);
if ($this->connection->has($file)) {
$this->connection->delete($file);
$this->cli->out("<red> × {$fileNo} of {$numberOfFilesToDelete} <white>{$file}");
} else {
$this->cli->out("<red> ! {$fileNo} of {$numberOfFilesToDelete} <white>{$file} not found");
}
}
}
// Delete Directories
if (count($dirsToDelete) > 0) {
foreach ($dirsToDelete as $dirNo => $dir) {
if ($this->currentSubmoduleName) {
$dir = $this->currentSubmoduleName . '/' . $dir;
}
$numberOfdirsToDelete = count($dirsToDelete);
$dirNo = str_pad(++$dirNo, strlen($numberOfdirsToDelete), ' ', STR_PAD_LEFT);
if ($this->connection->has($dir)) {
$this->connection->deleteDir($dir);
$this->cli->out("<red> × {$dirNo} of {$numberOfdirsToDelete} <white>{$dir}");
} else {
$this->cli->out("<red> ! {$dirNo} of {$numberOfdirsToDelete} <white>{$dir} not found");
}
}
}
if (count($filesToUpload) > 0 or count($filesToDelete) > 0) {
$this->setRevision($localRevision);
} else {
$this->cli->gray()->out(' No files to upload or delete.');
}
// If $this->revision is not HEAD, it means the rollback command was provided
// The working copy was rolled back earlier to run the deployment, and we
// now want to return the working copy back to its original state.
if ($this->revision != 'HEAD') {
$this->git->command('checkout ' . ($initialBranch ?: 'master'));
}
$this->log('[SHA: ' . $localRevision . '] Deployment to server: "' . $this->currentlyDeploying . '" from branch "' . $initialBranch . '". ' . count($filesToUpload) . ' files uploaded; ' . count($filesToDelete) . ' files deleted.');
}