3 namespace Robo\Task\Filesystem;
6 use Robo\Contract\RollbackInterface;
7 use Robo\Contract\BuilderAwareInterface;
8 use Robo\Common\BuilderAwareTrait;
11 * Create a temporary working directory that is automatically renamed to its
12 * final desired location if all of the tasks in the collection succeed. If
13 * there is a rollback, then the working directory is deleted.
17 * $collection = $this->collectionBuilder();
18 * $workingPath = $collection->workDir("build")->getPath();
19 * $collection->taskFilesystemStack()
20 * ->mkdir("$workingPath/log")
21 * ->touch("$workingPath/log/error.txt");
26 class WorkDir extends TmpDir implements RollbackInterface, BuilderAwareInterface
28 use BuilderAwareTrait;
33 protected $finalDestination;
36 * @param string $finalDestination
38 public function __construct($finalDestination)
40 $this->finalDestination = $finalDestination;
42 // Create a temporary directory to work in. We will place our
43 // temporary directory in the same location as the final destination
44 // directory, so that the work directory can be moved into place
45 // without having to be copied, e.g. in a cross-volume rename scenario.
46 parent::__construct(basename($finalDestination), dirname($finalDestination));
50 * Create our working directory.
52 * @return \Robo\Result
56 // Destination cannot be empty
57 if (empty($this->finalDestination)) {
58 return Result::error($this, "Destination directory not specified.");
61 // Before we do anything else, ensure that any directory in the
62 // final destination is writable, so that we can at a minimum
63 // move it out of the way before placing our results there.
64 if (is_dir($this->finalDestination)) {
65 if (!is_writable($this->finalDestination)) {
66 return Result::error($this, "Destination directory {dir} exists and cannot be overwritten.", ['dir' => $this->finalDestination]);
74 * Move our working directory into its final destination once the
75 * collection it belongs to completes.
77 public function complete()
79 $this->restoreWorkingDirectory();
81 // Delete the final destination, if it exists.
82 // Move it out of the way first, in case it cannot
83 // be completely deleted.
84 if (file_exists($this->finalDestination)) {
85 $temporaryLocation = static::randomLocation($this->finalDestination . '_TO_DELETE_');
86 // This should always work, because we already created a temporary
87 // folder in the parent directory of the final destination, and we
88 // have already checked to confirm that the final destination is
90 rename($this->finalDestination, $temporaryLocation);
91 // This may silently fail, leaving artifacts behind, if there
92 // are permissions problems with some items somewhere inside
93 // the folder being deleted.
94 $this->fs->remove($temporaryLocation);
97 // Move our working directory over the final destination.
98 // This should never be a cross-volume rename, so this should
100 $workDir = reset($this->dirs);
101 if (file_exists($workDir)) {
102 rename($workDir, $this->finalDestination);
107 * Delete our working directory
109 public function rollback()
111 $this->restoreWorkingDirectory();
112 $this->deleteTmpDir();
116 * Get a reference to the path to the temporary directory, so that
117 * it may be used to create other tasks. Note that the directory
118 * is not actually created until the task runs.
122 public function getPath()
124 return $this->dirs[0];