private function saveConfig() { $config = $this->getSource()->getAllKeys(); $json = new PhutilJSON(); $data = $json->encodeFormatted($config); Filesystem::writeFile($this->getConfigPath(), $data); }
/** * Create a temp file containing an SSL cert, and use it for this session. * * This allows us to do host-specific SSL certificates in whatever client * is using libphutil. e.g. in Arcanist, you could add an "ssl_cert" key * to a specific host in ~/.arcrc and use that. * * cURL needs this to be a file, it doesn't seem to be able to handle a string * which contains the cert. So we make a temporary file and store it there. * * @param string The multi-line, possibly lengthy, SSL certificate to use. * @return this */ public function setCABundleFromString($certificate) { $temp = new TempFile(); Filesystem::writeFile($temp, $certificate); $this->cabundle = $temp; return $this; }
function __phutil_signal_handler__($signal_number) { $e = new Exception(); $pid = getmypid(); // Some phabricator daemons may not be attached to a terminal. Filesystem::writeFile(sys_get_temp_dir() . '/phabricator_backtrace_' . $pid, $e->getTraceAsString()); }
public function setKeys(array $keys, $ttl = null) { $this->validateKeys(array_keys($keys)); $this->lockCache(15); if ($ttl) { $ttl_epoch = time() + $ttl; } else { $ttl_epoch = null; } foreach ($keys as $key => $value) { $dict = array('value' => $value); if ($ttl_epoch) { $dict['ttl'] = $ttl_epoch; } try { $key_file = $this->getKeyFile($key); $key_dir = dirname($key_file); if (!Filesystem::pathExists($key_dir)) { Filesystem::createDirectory($key_dir, $mask = 0755, $recursive = true); } $new_file = $key_file . '.new'; Filesystem::writeFile($new_file, serialize($dict)); Filesystem::rename($new_file, $key_file); } catch (FilesystemException $ex) { phlog($ex); } } $this->unlockCache(); return $this; }
public function handleSignal(PhutilSignalRouter $router, $signo) { $e = new Exception(); $pid = getmypid(); // Some Phabricator daemons may not be attached to a terminal. Filesystem::writeFile(sys_get_temp_dir() . '/phabricator_backtrace_' . $pid, $e->getTraceAsString()); }
/** * Launch an editor and edit the content. The edited content will be * returned. * * @return string Edited content. * @throws Exception The editor exited abnormally or something untoward * occurred. * * @task edit */ public function editInteractively() { $name = $this->getName(); $content = $this->getContent(); if (phutil_is_windows()) { $content = str_replace("\n", "\r\n", $content); } $tmp = Filesystem::createTemporaryDirectory('edit.'); $path = $tmp . DIRECTORY_SEPARATOR . $name; try { Filesystem::writeFile($path, $content); } catch (Exception $ex) { Filesystem::remove($tmp); throw $ex; } $editor = $this->getEditor(); $offset = $this->getLineOffset(); $err = $this->invokeEditor($editor, $path, $offset); if ($err) { Filesystem::remove($tmp); throw new Exception("Editor exited with an error code (#{$err})."); } try { $result = Filesystem::readFile($path); Filesystem::remove($tmp); } catch (Exception $ex) { Filesystem::remove($tmp); throw $ex; } if (phutil_is_windows()) { $result = str_replace("\r\n", "\n", $result); } $this->setContent($result); return $this->getContent(); }
protected function executeChecks() { if (phutil_is_windows()) { $bin_name = 'where'; } else { $bin_name = 'which'; } if (!Filesystem::binaryExists($bin_name)) { $message = pht("Without '%s', Phabricator can not test for the availability " . "of other binaries.", $bin_name); $this->raiseWarning($bin_name, $message); // We need to return here if we can't find the 'which' / 'where' binary // because the other tests won't be valid. return; } if (!Filesystem::binaryExists('diff')) { $message = pht("Without 'diff', Phabricator will not be able to generate or render " . "diffs in multiple applications."); $this->raiseWarning('diff', $message); } else { $tmp_a = new TempFile(); $tmp_b = new TempFile(); $tmp_c = new TempFile(); Filesystem::writeFile($tmp_a, 'A'); Filesystem::writeFile($tmp_b, 'A'); Filesystem::writeFile($tmp_c, 'B'); list($err) = exec_manual('diff %s %s', $tmp_a, $tmp_b); if ($err) { $this->newIssue('bin.diff.same')->setName(pht("Unexpected 'diff' Behavior"))->setMessage(pht("The 'diff' binary on this system has unexpected behavior: " . "it was expected to exit without an error code when passed " . "identical files, but exited with code %d.", $err)); } list($err) = exec_manual('diff %s %s', $tmp_a, $tmp_c); if (!$err) { $this->newIssue('bin.diff.diff')->setName(pht("Unexpected 'diff' Behavior"))->setMessage(pht("The 'diff' binary on this system has unexpected behavior: " . "it was expected to exit with a nonzero error code when passed " . "differing files, but did not.")); } } $table = new PhabricatorRepository(); $vcses = queryfx_all($table->establishConnection('r'), 'SELECT DISTINCT versionControlSystem FROM %T', $table->getTableName()); foreach ($vcses as $vcs) { switch ($vcs['versionControlSystem']) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: $binary = 'git'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: $binary = 'svn'; break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: $binary = 'hg'; break; default: $binary = null; break; } if (!$binary) { continue; } if (!Filesystem::binaryExists($binary)) { $message = pht('You have at least one repository configured which uses this ' . 'version control system. It will not work without the VCS binary.'); $this->raiseWarning($binary, $message); } } }
public function writeFile($path, $data) { $source = new TempFile(); Filesystem::writeFile($source, $data); $future = $this->getExecFuture($path); $future->write(csprintf('put %s %s', $source, $path)); $future->resolvex(); }
/** * Generate a raw diff from two raw files. This is a lower-level API than * @{method:generateChangesetFromFileContent}, but may be useful if you need * to use a custom parser configuration, as with Diffusion. * * @param string Entire previous file content. * @param string Entire current file content. * @return string Raw diff between the two files. * @task diff */ public function generateRawDiffFromFileContent($old, $new) { $options = array(); if ($this->ignoreWhitespace) { $options[] = '-bw'; } // Generate diffs with full context. $options[] = '-U65535'; $old_name = nonempty($this->oldName, '/dev/universe') . ' 9999-99-99'; $new_name = nonempty($this->newName, '/dev/universe') . ' 9999-99-99'; $options[] = '-L'; $options[] = $old_name; $options[] = '-L'; $options[] = $new_name; $old_tmp = new TempFile(); $new_tmp = new TempFile(); Filesystem::writeFile($old_tmp, $old); Filesystem::writeFile($new_tmp, $new); list($err, $diff) = exec_manual('diff %Ls %s %s', $options, $old_tmp, $new_tmp); if (!$err) { // This indicates that the two files are the same (or, possibly, the // same modulo whitespace differences, which is why we can't do this // check trivially before running `diff`). Build a synthetic, changeless // diff so that we can still render the raw, unchanged file instead of // being forced to just say "this file didn't change" since we don't have // the content. $entire_file = explode("\n", $old); foreach ($entire_file as $k => $line) { $entire_file[$k] = ' ' . $line; } $len = count($entire_file); $entire_file = implode("\n", $entire_file); // TODO: If both files were identical but missing newlines, we probably // get this wrong. Unclear if it ever matters. // This is a bit hacky but the diff parser can handle it. $diff = "--- {$old_name}\n" . "+++ {$new_name}\n" . "@@ -1,{$len} +1,{$len} @@\n" . $entire_file . "\n"; } else { if ($this->ignoreWhitespace) { // Under "-bw", `diff` is inconsistent about emitting "\ No newline // at end of file". For instance, a long file with a change in the // middle will emit a contextless "\ No newline..." at the end if a // newline is removed, but not if one is added. A file with a change // at the end will emit the "old" "\ No newline..." block only, even // if the newline was not removed. Since we're ostensibly ignoring // whitespace changes, just drop these lines if they appear anywhere // in the diff. $lines = explode("\n", $diff); foreach ($lines as $key => $line) { if (isset($line[0]) && $line[0] == '\\') { unset($lines[$key]); } } $diff = implode("\n", $lines); } } return $diff; }
public function __construct($filename = null, $dir = null) { $this->dir = Filesystem::createTemporaryDirectory(); if ($filename === null) { $this->file = tempnam($this->dir, getmypid() . '-'); } else { $this->file = $this->dir . '/' . $filename; } Filesystem::writeFile($this, ''); }
private function writeAndRead($write, $read, $delimiter = "\n") { $tmp = new TempFile(); Filesystem::writeFile($tmp, $write); $lines = array(); $iterator = id(new LinesOfALargeFile($tmp))->setDelimiter($delimiter); foreach ($iterator as $n => $line) { $lines[$n - 1] = $line; } $this->assertEqual($read, $lines, "Write: " . phutil_utf8_shorten($write, 32)); }
private function writeAndRead($write, $read, $delimiter = "\n") { $tmp = new TempFile(); Filesystem::writeFile($tmp, $write); $lines = array(); $iterator = id(new LinesOfALargeFile($tmp))->setDelimiter($delimiter); foreach ($iterator as $n => $line) { $lines[$n - 1] = $line; } $this->assertEqual($read, $lines, 'Write: ' . id(new PhutilUTF8StringTruncator())->setMaximumGlyphs(32)->truncateString($write)); }
public static function renderDifferences($old, $new, $context_lines = 3, $diff_options = "-L 'Old Value' -L 'New Value'") { if ((string) $old === (string) $new) { $new .= "\n(Old and new values are identical.)"; } $file_old = new TempFile(); $file_new = new TempFile(); Filesystem::writeFile($file_old, (string) $old . "\n"); Filesystem::writeFile($file_new, (string) $new . "\n"); list($err, $stdout) = exec_manual('diff %C -U %s %s %s', $diff_options, $context_lines, $file_old, $file_new); return $stdout; }
/** * Create a new temporary file. * * @param string? Filename hint. This is useful if you intend to edit the * file with an interactive editor, so the user's editor shows * "commit-message" instead of "p3810hf-1z9b89bas". * @task create */ public function __construct($filename = null) { $this->dir = Filesystem::createTemporaryDirectory(); if ($filename === null) { $this->file = tempnam($this->dir, getmypid() . '-'); } else { $this->file = $this->dir . '/' . $filename; } // If we fatal (e.g., call a method on NULL), destructors are not called. // Make sure our destructor is invoked. register_shutdown_function(array($this, '__destruct')); Filesystem::writeFile($this, ''); }
public function execute(PhutilArgumentParser $args) { $styles = PhabricatorSyntaxStyle::getAllStyles(); $root = dirname(phutil_get_library_root('phabricator')); $root = $root . '/webroot/rsrc/css/syntax/'; foreach ($styles as $key => $style) { $content = $this->generateCSS($style); $path = $root . '/syntax-' . $key . '.css'; Filesystem::writeFile($path, $content); echo tsprintf("%s\n", pht('Rebuilt "%s" syntax CSS.', basename($path))); } return 0; }
public function decryptSecret(PhutilOpaqueEnvelope $secret, PhutilOpaqueEnvelope $password) { $tmp = new TempFile(); Filesystem::writeFile($tmp, $secret->openEnvelope()); if (!Filesystem::binaryExists('ssh-keygen')) { throw new Exception(pht('Decrypting SSH keys requires the `ssh-keygen` binary, but it ' . 'is not available in PATH. Either make it available or strip the ' . 'password fromt his SSH key manually before uploading it.')); } list($err, $stdout, $stderr) = exec_manual('ssh-keygen -p -P %P -N %s -f %s', $password, '', (string) $tmp); if ($err) { return null; } else { return new PhutilOpaqueEnvelope(Filesystem::readFile($tmp)); } }
protected final function launch($debug = false) { $console = PhutilConsole::getConsole(); if ($debug) { $console->writeOut(pht("Starting Aphlict server in foreground...\n")); } else { Filesystem::writeFile($this->getPIDPath(), getmypid()); } $server_uri = PhabricatorEnv::getEnvConfig('notification.server-uri'); $server_uri = new PhutilURI($server_uri); $client_uri = PhabricatorEnv::getEnvConfig('notification.client-uri'); $client_uri = new PhutilURI($client_uri); $user = PhabricatorEnv::getEnvConfig('notification.user'); $log = PhabricatorEnv::getEnvConfig('notification.log'); $server_argv = array(); $server_argv[] = csprintf('--port=%s', $client_uri->getPort()); $server_argv[] = csprintf('--admin=%s', $server_uri->getPort()); $server_argv[] = csprintf('--host=%s', $server_uri->getDomain()); if ($user) { $server_argv[] = csprintf('--user=%s', $user); } if (!$debug) { $server_argv[] = csprintf('--log=%s', $log); } $command = csprintf('%s %s %C', $this->getNodeBinary(), dirname(__FILE__) . '/../../../../support/aphlict/server/aphlict_server.js', implode(' ', $server_argv)); if (!$debug) { declare (ticks=1); pcntl_signal(SIGINT, array($this, 'cleanup')); pcntl_signal(SIGTERM, array($this, 'cleanup')); } register_shutdown_function(array($this, 'cleanup')); if ($debug) { $console->writeOut("Launching server:\n\n \$ " . $command . "\n\n"); $err = phutil_passthru('%C', $command); $console->writeOut(">>> Server exited!\n"); exit($err); } else { while (true) { global $g_future; $g_future = new ExecFuture('exec %C', $command); $g_future->resolve(); // If the server exited, wait a couple of seconds and restart it. unset($g_future); sleep(2); } } }
/** * Rebuild the resource map for a resource source. * * @param CelerityPhysicalResources Resource source to rebuild. * @return void */ private function rebuildResources(CelerityPhysicalResources $resources) { $this->log(pht('Rebuilding resource source "%s" (%s)...', $resources->getName(), get_class($resources))); $binary_map = $this->rebuildBinaryResources($resources); $this->log(pht('Found %d binary resources.', new PhutilNumber(count($binary_map)))); $xformer = id(new CelerityResourceTransformer())->setMinify(false)->setRawURIMap(ipull($binary_map, 'uri')); $text_map = $this->rebuildTextResources($resources, $xformer); $this->log(pht('Found %d text resources.', new PhutilNumber(count($text_map)))); $resource_graph = array(); $requires_map = array(); $symbol_map = array(); foreach ($text_map as $name => $info) { if (isset($info['provides'])) { $symbol_map[$info['provides']] = $info['hash']; // We only need to check for cycles and add this to the requires map // if it actually requires anything. if (!empty($info['requires'])) { $resource_graph[$info['provides']] = $info['requires']; $requires_map[$info['hash']] = $info['requires']; } } } $this->detectGraphCycles($resource_graph); $name_map = ipull($binary_map, 'hash') + ipull($text_map, 'hash'); $hash_map = array_flip($name_map); $package_map = $this->rebuildPackages($resources, $symbol_map, $hash_map); $this->log(pht('Found %d packages.', new PhutilNumber(count($package_map)))); $component_map = array(); foreach ($package_map as $package_name => $package_info) { foreach ($package_info['symbols'] as $symbol) { $component_map[$symbol] = $package_name; } } $name_map = $this->mergeNameMaps(array(array(pht('Binary'), ipull($binary_map, 'hash')), array(pht('Text'), ipull($text_map, 'hash')), array(pht('Package'), ipull($package_map, 'hash')))); $package_map = ipull($package_map, 'symbols'); ksort($name_map); ksort($symbol_map); ksort($requires_map); ksort($package_map); $map_content = $this->formatMapContent(array('names' => $name_map, 'symbols' => $symbol_map, 'requires' => $requires_map, 'packages' => $package_map)); $map_path = $resources->getPathToMap(); $this->log(pht('Writing map "%s".', Filesystem::readablePath($map_path))); Filesystem::writeFile($map_path, $map_content); }
public function getKeyfileEnvelope() { $credential = $this->requireCredential(); $file_type = PassphraseCredentialTypeSSHPrivateKeyFile::CREDENTIAL_TYPE; if ($credential->getCredentialType() != $file_type) { // If the credential does not store a file, write the key text out to a // temporary file so we can pass it to `ssh`. if (!$this->keyFile) { $secret = $credential->getSecret(); if (!$secret) { throw new Exception(pht('Attempting to use a credential ("%s") but the credential ' . 'secret has been destroyed!', $credential->getMonogram())); } $temporary_file = new TempFile('passphrase-ssh-key'); Filesystem::changePermissions($temporary_file, 0600); Filesystem::writeFile($temporary_file, $secret->openEnvelope()); $this->keyFile = $temporary_file; } return new PhutilOpaqueEnvelope((string) $this->keyFile); } return $credential->getSecret(); }
/** * Write the file data to local disk. Returns the relative path as the * file data handle. * @task impl */ public function writeFile($data, array $params) { $root = $this->getLocalDiskFileStorageRoot(); // Generate a random, unique file path like "ab/29/1f918a9ac39201ff". We // put a couple of subdirectories up front to avoid a situation where we // have one directory with a zillion files in it, since this is generally // bad news. do { $name = md5(mt_rand()); $name = preg_replace('/^(..)(..)(.*)$/', '\\1/\\2/\\3', $name); if (!Filesystem::pathExists($root . '/' . $name)) { break; } } while (true); $parent = $root . '/' . dirname($name); if (!Filesystem::pathExists($parent)) { execx('mkdir -p %s', $parent); } Filesystem::writeFile($root . '/' . $name, $data); return $name; }
public static function rebuildPackages($root) { $packages = JavelinSyncSpec::getPackageMap(); $data = array(); foreach ($packages as $package => $items) { $content = array(); foreach ($items as $item) { if (empty($data[$item])) { $data[$item] = Filesystem::readFile($root . '/src/' . $item); } $content[] = $data[$item]; } $content = implode("\n\n", $content); echo "Writing {$package}.dev.js...\n"; Filesystem::writeFile($root . '/pkg/' . $package . '.dev.js', $content); echo "Writing {$package}.min.js...\n"; $exec = new ExecFuture($root . '/support/jsxmin/jsxmin __DEV__:0'); $exec->write($content); list($stdout) = $exec->resolvex(); Filesystem::writeFile($root . '/pkg/' . $package . '.min.js', $stdout); } }
public function writePatchToDisk() { $path = $this->lintResult->getFilePathOnDisk(); $data = $this->getModifiedFileContent(); $ii = null; do { $lint = $path . '.linted' . $ii++; } while (file_exists($lint)); // Copy existing file to preserve permissions. 'chmod --reference' is not // supported under OSX. if (Filesystem::pathExists($path)) { // This path may not exist if we're generating a new file. execx('cp -p %s %s', $path, $lint); } Filesystem::writeFile($lint, $data); list($err) = exec_manual('mv -f %s %s', $lint, $path); if ($err) { throw new Exception("Unable to overwrite path `{$path}', patched version was left " . "at `{$lint}'."); } foreach ($this->applyMessages as $message) { $message->didApplyPatch(); } }
public function writePatchToDisk() { $path = $this->lintResult->getFilePathOnDisk(); $data = $this->getModifiedFileContent(); $ii = null; do { $lint = $path . '.linted' . $ii++; } while (file_exists($lint)); // Copy existing file to preserve permissions. 'chmod --reference' is not // supported under OSX. if (Filesystem::pathExists($path)) { // This path may not exist if we're generating a new file. Filesystem::copyFile($path, $lint); } Filesystem::writeFile($lint, $data); try { Filesystem::rename($lint, $path); } catch (FilesystemException $e) { throw new Exception(pht("Unable to overwrite path '%s', patched version was left at '%s'.", $path, $lint)); } foreach ($this->applyMessages as $message) { $message->didApplyPatch(); } }
public function run() { $source = $this->getSource(); $param = $this->getSourceParam(); try { switch ($source) { case self::SOURCE_PATCH: if ($param == '-') { $patch = @file_get_contents('php://stdin'); if (!strlen($patch)) { throw new ArcanistUsageException(pht('Failed to read patch from stdin!')); } } else { $patch = Filesystem::readFile($param); } $bundle = ArcanistBundle::newFromDiff($patch); break; case self::SOURCE_BUNDLE: $path = $this->getArgument('arcbundle'); $bundle = ArcanistBundle::newFromArcBundle($path); break; case self::SOURCE_REVISION: $bundle = $this->loadRevisionBundleFromConduit($this->getConduit(), $param); break; case self::SOURCE_DIFF: $bundle = $this->loadDiffBundleFromConduit($this->getConduit(), $param); break; } } catch (ConduitClientException $ex) { if ($ex->getErrorCode() == 'ERR-INVALID-SESSION') { // Phabricator is not configured to allow anonymous access to // Differential. $this->authenticateConduit(); return $this->run(); } else { throw $ex; } } $try_encoding = nonempty($this->getArgument('encoding'), null); if (!$try_encoding) { if ($this->requiresConduit()) { try { $try_encoding = $this->getRepositoryEncoding(); } catch (ConduitClientException $e) { $try_encoding = null; } } } if ($try_encoding) { $bundle->setEncoding($try_encoding); } $sanity_check = !$this->getArgument('force', false); // we should update the working copy before we do ANYTHING else to // the working copy if ($this->shouldUpdateWorkingCopy()) { $this->updateWorkingCopy(); } if ($sanity_check) { $this->requireCleanWorkingCopy(); } $repository_api = $this->getRepositoryAPI(); $has_base_revision = $repository_api->hasLocalCommit($bundle->getBaseRevision()); if ($this->canBranch() && ($this->shouldBranch() || $this->shouldCommit() && $has_base_revision)) { if ($repository_api instanceof ArcanistGitAPI) { $original_branch = $repository_api->getBranchName(); } else { if ($repository_api instanceof ArcanistMercurialAPI) { $original_branch = $repository_api->getActiveBookmark(); } } // If we weren't on a branch, then record the ref we'll return to // instead. if ($original_branch === null) { if ($repository_api instanceof ArcanistGitAPI) { $original_branch = $repository_api->getCanonicalRevisionName('HEAD'); } else { if ($repository_api instanceof ArcanistMercurialAPI) { $original_branch = $repository_api->getCanonicalRevisionName('.'); } } } $new_branch = $this->createBranch($bundle, $has_base_revision); } if (!$has_base_revision && $this->shouldApplyDependencies()) { $this->applyDependencies($bundle); } if ($sanity_check) { $this->sanityCheck($bundle); } if ($repository_api instanceof ArcanistSubversionAPI) { $patch_err = 0; $copies = array(); $deletes = array(); $patches = array(); $propset = array(); $adds = array(); $symlinks = array(); $changes = $bundle->getChanges(); foreach ($changes as $change) { $type = $change->getType(); $should_patch = true; $filetype = $change->getFileType(); switch ($filetype) { case ArcanistDiffChangeType::FILE_SYMLINK: $should_patch = false; $symlinks[] = $change; break; } switch ($type) { case ArcanistDiffChangeType::TYPE_MOVE_AWAY: case ArcanistDiffChangeType::TYPE_MULTICOPY: case ArcanistDiffChangeType::TYPE_DELETE: $path = $change->getCurrentPath(); $fpath = $repository_api->getPath($path); if (!@file_exists($fpath)) { $ok = phutil_console_confirm(pht("Patch deletes file '%s', but the file does not exist in " . "the working copy. Continue anyway?", $path)); if (!$ok) { throw new ArcanistUserAbortException(); } } else { $deletes[] = $change->getCurrentPath(); } $should_patch = false; break; case ArcanistDiffChangeType::TYPE_COPY_HERE: case ArcanistDiffChangeType::TYPE_MOVE_HERE: $path = $change->getOldPath(); $fpath = $repository_api->getPath($path); if (!@file_exists($fpath)) { $cpath = $change->getCurrentPath(); if ($type == ArcanistDiffChangeType::TYPE_COPY_HERE) { $verbs = pht('copies'); } else { $verbs = pht('moves'); } $ok = phutil_console_confirm(pht("Patch %s '%s' to '%s', but source path does not exist " . "in the working copy. Continue anyway?", $verbs, $path, $cpath)); if (!$ok) { throw new ArcanistUserAbortException(); } } else { $copies[] = array($change->getOldPath(), $change->getCurrentPath()); } break; case ArcanistDiffChangeType::TYPE_ADD: $adds[] = $change->getCurrentPath(); break; } if ($should_patch) { $cbundle = ArcanistBundle::newFromChanges(array($change)); $patches[$change->getCurrentPath()] = $cbundle->toUnifiedDiff(); $prop_old = $change->getOldProperties(); $prop_new = $change->getNewProperties(); $props = $prop_old + $prop_new; foreach ($props as $key => $ignored) { if (idx($prop_old, $key) !== idx($prop_new, $key)) { $propset[$change->getCurrentPath()][$key] = idx($prop_new, $key); } } } } // Before we start doing anything, create all the directories we're going // to add files to if they don't already exist. foreach ($copies as $copy) { list($src, $dst) = $copy; $this->createParentDirectoryOf($dst); } foreach ($patches as $path => $patch) { $this->createParentDirectoryOf($path); } foreach ($adds as $add) { $this->createParentDirectoryOf($add); } // TODO: The SVN patch workflow likely does not work on windows because // of the (cd ...) stuff. foreach ($copies as $copy) { list($src, $dst) = $copy; passthru(csprintf('(cd %s; svn cp %s %s)', $repository_api->getPath(), ArcanistSubversionAPI::escapeFileNameForSVN($src), ArcanistSubversionAPI::escapeFileNameForSVN($dst))); } foreach ($deletes as $delete) { passthru(csprintf('(cd %s; svn rm %s)', $repository_api->getPath(), ArcanistSubversionAPI::escapeFileNameForSVN($delete))); } foreach ($symlinks as $symlink) { $link_target = $symlink->getSymlinkTarget(); $link_path = $symlink->getCurrentPath(); switch ($symlink->getType()) { case ArcanistDiffChangeType::TYPE_ADD: case ArcanistDiffChangeType::TYPE_CHANGE: case ArcanistDiffChangeType::TYPE_MOVE_HERE: case ArcanistDiffChangeType::TYPE_COPY_HERE: execx('(cd %s && ln -sf %s %s)', $repository_api->getPath(), $link_target, $link_path); break; } } foreach ($patches as $path => $patch) { $err = null; if ($patch) { $tmp = new TempFile(); Filesystem::writeFile($tmp, $patch); passthru(csprintf('(cd %s; patch -p0 < %s)', $repository_api->getPath(), $tmp), $err); } else { passthru(csprintf('(cd %s; touch %s)', $repository_api->getPath(), $path), $err); } if ($err) { $patch_err = max($patch_err, $err); } } foreach ($adds as $add) { passthru(csprintf('(cd %s; svn add %s)', $repository_api->getPath(), ArcanistSubversionAPI::escapeFileNameForSVN($add))); } foreach ($propset as $path => $changes) { foreach ($changes as $prop => $value) { if ($prop == 'unix:filemode') { // Setting this property also changes the file mode. $prop = 'svn:executable'; $value = octdec($value) & 0111 ? 'on' : null; } if ($value === null) { passthru(csprintf('(cd %s; svn propdel %s %s)', $repository_api->getPath(), $prop, ArcanistSubversionAPI::escapeFileNameForSVN($path))); } else { passthru(csprintf('(cd %s; svn propset %s %s %s)', $repository_api->getPath(), $prop, $value, ArcanistSubversionAPI::escapeFileNameForSVN($path))); } } } if ($patch_err == 0) { echo phutil_console_format("<bg:green>** %s **</bg> %s\n", pht('OKAY'), pht('Successfully applied patch to the working copy.')); } else { echo phutil_console_format("\n\n<bg:yellow>** %s **</bg> %s\n", pht('WARNING'), pht("Some hunks could not be applied cleanly by the unix '%s' " . "utility. Your working copy may be different from the revision's " . "base, or you may be in the wrong subdirectory. You can export " . "the raw patch file using '%s', and then try to apply it by " . "fiddling with options to '%s' (particularly, %s), or manually. " . "The output above, from '%s', may be helpful in " . "figuring out what went wrong.", 'patch', 'arc export --unified', 'patch', '-p', 'patch')); } return $patch_err; } else { if ($repository_api instanceof ArcanistGitAPI) { $patchfile = new TempFile(); Filesystem::writeFile($patchfile, $bundle->toGitPatch()); $passthru = new PhutilExecPassthru('git apply --index --reject -- %s', $patchfile); $passthru->setCWD($repository_api->getPath()); $err = $passthru->execute(); if ($err) { echo phutil_console_format("\n<bg:red>** %s **</bg>\n", pht('Patch Failed!')); // NOTE: Git patches may fail if they change the case of a filename // (for instance, from 'example.c' to 'Example.c'). As of now, Git // can not apply these patches on case-insensitive filesystems and // there is no way to build a patch which works. throw new ArcanistUsageException(pht('Unable to apply patch!')); } // in case there were any submodule changes involved $repository_api->execpassthru('submodule update --init --recursive'); if ($this->shouldCommit()) { if ($bundle->getFullAuthor()) { $author_cmd = csprintf('--author=%s', $bundle->getFullAuthor()); } else { $author_cmd = ''; } $commit_message = $this->getCommitMessage($bundle); $future = $repository_api->execFutureLocal('commit -a %C -F - --no-verify', $author_cmd); $future->write($commit_message); $future->resolvex(); $verb = pht('committed'); } else { $verb = pht('applied'); } if ($this->canBranch() && !$this->shouldBranch() && $this->shouldCommit() && $has_base_revision) { $repository_api->execxLocal('checkout %s', $original_branch); $ex = null; try { $repository_api->execxLocal('cherry-pick %s', $new_branch); } catch (Exception $ex) { // do nothing } $repository_api->execxLocal('branch -D %s', $new_branch); if ($ex) { echo phutil_console_format("\n<bg:red>** %s**</bg>\n", pht('Cherry Pick Failed!')); throw $ex; } } echo phutil_console_format("<bg:green>** %s **</bg> %s\n", pht('OKAY'), pht('Successfully %s patch.', $verb)); } else { if ($repository_api instanceof ArcanistMercurialAPI) { $future = $repository_api->execFutureLocal('import --no-commit -'); $future->write($bundle->toGitPatch()); try { $future->resolvex(); } catch (CommandException $ex) { echo phutil_console_format("\n<bg:red>** %s **</bg>\n", pht('Patch Failed!')); $stderr = $ex->getStdErr(); if (preg_match('/case-folding collision/', $stderr)) { echo phutil_console_wrap(phutil_console_format("\n<bg:yellow>** %s **</bg> %s\n", pht('WARNING'), pht("This patch may have failed because it attempts to change " . "the case of a filename (for instance, from '%s' to '%s'). " . "Mercurial cannot apply patches like this on case-insensitive " . "filesystems. You must apply this patch manually.", 'example.c', 'Example.c'))); } throw $ex; } if ($this->shouldCommit()) { $author = coalesce($bundle->getFullAuthor(), $bundle->getAuthorName()); if ($author !== null) { $author_cmd = csprintf('-u %s', $author); } else { $author_cmd = ''; } $commit_message = $this->getCommitMessage($bundle); $future = $repository_api->execFutureLocal('commit %C -l -', $author_cmd); $future->write($commit_message); $future->resolvex(); if (!$this->shouldBranch() && $has_base_revision) { $original_rev = $repository_api->getCanonicalRevisionName($original_branch); $current_parent = $repository_api->getCanonicalRevisionName(hgsprintf('%s^', $new_branch)); $err = 0; if ($original_rev != $current_parent) { list($err) = $repository_api->execManualLocal('rebase --dest %s --rev %s', hgsprintf('%s', $original_branch), hgsprintf('%s', $new_branch)); } $repository_api->execxLocal('bookmark --delete %s', $new_branch); if ($err) { $repository_api->execManualLocal('rebase --abort'); throw new ArcanistUsageException(phutil_console_format("\n<bg:red>** %s**</bg>\n", pht('Rebase onto %s failed!', $original_branch))); } } $verb = pht('committed'); } else { $verb = pht('applied'); } echo phutil_console_format("<bg:green>** %s **</bg> %s\n", pht('OKAY'), pht('Successfully %s patch.', $verb)); } else { throw new Exception(pht('Unknown version control system.')); } } } return 0; }
public function amendCommit($message = null) { if ($message === null) { $this->execxLocal('commit --amend --allow-empty -C HEAD'); } else { $tmp_file = new TempFile(); Filesystem::writeFile($tmp_file, $message); $this->execxLocal('commit --amend --allow-empty -F %s', $tmp_file); } $this->reloadWorkingCopy(); return $this; }
public function run() { $console = PhutilConsole::getConsole(); $working_copy = $this->getWorkingCopy(); $configuration_manager = $this->getConfigurationManager(); $engine = $this->newLintEngine($this->getArgument('engine')); $rev = $this->getArgument('rev'); $paths = $this->getArgument('paths'); $use_cache = $this->getArgument('cache', null); $everything = $this->getArgument('everything'); if ($everything && $paths) { throw new ArcanistUsageException(pht('You can not specify paths with %s. The %s flag lints every file.', '--everything', '--everything')); } if ($use_cache === null) { $use_cache = (bool) $configuration_manager->getConfigFromAnySource('arc.lint.cache', false); } if ($rev && $paths) { throw new ArcanistUsageException(pht('Specify either %s or paths.', '--rev')); } // NOTE: When the user specifies paths, we imply --lintall and show all // warnings for the paths in question. This is easier to deal with for // us and less confusing for users. $this->shouldLintAll = $paths ? true : false; if ($this->getArgument('lintall')) { $this->shouldLintAll = true; } else { if ($this->getArgument('only-changed')) { $this->shouldLintAll = false; } } if ($everything) { $paths = iterator_to_array($this->getRepositoryApi()->getAllFiles()); $this->shouldLintAll = true; } else { $paths = $this->selectPathsForWorkflow($paths, $rev); } $this->engine = $engine; $engine->setMinimumSeverity($this->getArgument('severity', self::DEFAULT_SEVERITY)); $file_hashes = array(); if ($use_cache) { $engine->setRepositoryVersion($this->getRepositoryVersion()); $cache = $this->readScratchJSONFile('lint-cache.json'); $cache = idx($cache, $this->getCacheKey(), array()); $cached = array(); foreach ($paths as $path) { $abs_path = $engine->getFilePathOnDisk($path); if (!Filesystem::pathExists($abs_path)) { continue; } $file_hashes[$abs_path] = md5_file($abs_path); if (!isset($cache[$path])) { continue; } $messages = idx($cache[$path], $file_hashes[$abs_path]); if ($messages !== null) { $cached[$path] = $messages; } } if ($cached) { $console->writeErr("%s\n", pht("Using lint cache, use '%s' to disable it.", '--cache 0')); } $engine->setCachedResults($cached); } // Propagate information about which lines changed to the lint engine. // This is used so that the lint engine can drop warning messages // concerning lines that weren't in the change. $engine->setPaths($paths); if (!$this->shouldLintAll) { foreach ($paths as $path) { // Note that getChangedLines() returns null to indicate that a file // is binary or a directory (i.e., changed lines are not relevant). $engine->setPathChangedLines($path, $this->getChangedLines($path, 'new')); } } // Enable possible async linting only for 'arc diff' not 'arc lint' if ($this->getParentWorkflow()) { $engine->setEnableAsyncLint(true); } else { $engine->setEnableAsyncLint(false); } if ($this->getArgument('only-new')) { $conduit = $this->getConduit(); $api = $this->getRepositoryAPI(); if ($rev) { $api->setBaseCommit($rev); } $svn_root = id(new PhutilURI($api->getSourceControlPath()))->getPath(); $all_paths = array(); foreach ($paths as $path) { $path = str_replace(DIRECTORY_SEPARATOR, '/', $path); $full_paths = array($path); $change = $this->getChange($path); $type = $change->getType(); if (ArcanistDiffChangeType::isOldLocationChangeType($type)) { $full_paths = $change->getAwayPaths(); } else { if (ArcanistDiffChangeType::isNewLocationChangeType($type)) { continue; } else { if (ArcanistDiffChangeType::isDeleteChangeType($type)) { continue; } } } foreach ($full_paths as $full_path) { $all_paths[$svn_root . '/' . $full_path] = $path; } } $lint_future = $conduit->callMethod('diffusion.getlintmessages', array('repositoryPHID' => idx($this->loadProjectRepository(), 'phid'), 'branch' => '', 'commit' => $api->getBaseCommit(), 'files' => array_keys($all_paths))); } $failed = null; try { $engine->run(); } catch (Exception $ex) { $failed = $ex; } $results = $engine->getResults(); if ($this->getArgument('only-new')) { $total = 0; foreach ($results as $result) { $total += count($result->getMessages()); } // Don't wait for response with default value of --only-new. $timeout = null; if ($this->getArgument('only-new') === null || !$total) { $timeout = 0; } $raw_messages = $this->resolveCall($lint_future, $timeout); if ($raw_messages && $total) { $old_messages = array(); $line_maps = array(); foreach ($raw_messages as $message) { $path = $all_paths[$message['path']]; $line = $message['line']; $code = $message['code']; if (!isset($line_maps[$path])) { $line_maps[$path] = $this->getChange($path)->buildLineMap(); } $new_lines = idx($line_maps[$path], $line); if (!$new_lines) { // Unmodified lines after last hunk. $last_old = $line_maps[$path] ? last_key($line_maps[$path]) : 0; $news = array_filter($line_maps[$path]); $last_new = $news ? last(end($news)) : 0; $new_lines = array($line + $last_new - $last_old); } $error = array($code => array(true)); foreach ($new_lines as $new) { if (isset($old_messages[$path][$new])) { $old_messages[$path][$new][$code][] = true; break; } $old_messages[$path][$new] =& $error; } unset($error); } foreach ($results as $result) { foreach ($result->getMessages() as $message) { $path = str_replace(DIRECTORY_SEPARATOR, '/', $message->getPath()); $line = $message->getLine(); $code = $message->getCode(); if (!empty($old_messages[$path][$line][$code])) { $message->setObsolete(true); array_pop($old_messages[$path][$line][$code]); } } $result->sortAndFilterMessages(); } } } if ($this->getArgument('never-apply-patches')) { $apply_patches = false; } else { $apply_patches = true; } if ($this->getArgument('apply-patches')) { $prompt_patches = false; } else { $prompt_patches = true; } if ($this->getArgument('amend-all')) { $this->shouldAmendChanges = true; $this->shouldAmendWithoutPrompt = true; } if ($this->getArgument('amend-autofixes')) { $prompt_autofix_patches = false; $this->shouldAmendChanges = true; $this->shouldAmendAutofixesWithoutPrompt = true; } else { $prompt_autofix_patches = true; } $repository_api = $this->getRepositoryAPI(); if ($this->shouldAmendChanges) { $this->shouldAmendChanges = $repository_api->supportsAmend() && !$this->isHistoryImmutable(); } $wrote_to_disk = false; switch ($this->getArgument('output')) { case 'json': $renderer = new ArcanistJSONLintRenderer(); $prompt_patches = false; $apply_patches = $this->getArgument('apply-patches'); break; case 'summary': $renderer = new ArcanistSummaryLintRenderer(); break; case 'none': $prompt_patches = false; $apply_patches = $this->getArgument('apply-patches'); $renderer = new ArcanistNoneLintRenderer(); break; case 'compiler': $renderer = new ArcanistCompilerLintRenderer(); $prompt_patches = false; $apply_patches = $this->getArgument('apply-patches'); break; case 'xml': $renderer = new ArcanistCheckstyleXMLLintRenderer(); $prompt_patches = false; $apply_patches = $this->getArgument('apply-patches'); break; default: $renderer = new ArcanistConsoleLintRenderer(); $renderer->setShowAutofixPatches($prompt_autofix_patches); break; } $all_autofix = true; $tmp = null; if ($this->getArgument('outfile') !== null) { $tmp = id(new TempFile())->setPreserveFile(true); } $preamble = $renderer->renderPreamble(); if ($tmp) { Filesystem::appendFile($tmp, $preamble); } else { $console->writeOut('%s', $preamble); } foreach ($results as $result) { $result_all_autofix = $result->isAllAutofix(); if (!$result->getMessages() && !$result_all_autofix) { continue; } if (!$result_all_autofix) { $all_autofix = false; } $lint_result = $renderer->renderLintResult($result); if ($lint_result) { if ($tmp) { Filesystem::appendFile($tmp, $lint_result); } else { $console->writeOut('%s', $lint_result); } } if ($apply_patches && $result->isPatchable()) { $patcher = ArcanistLintPatcher::newFromArcanistLintResult($result); $old_file = $result->getFilePathOnDisk(); if ($prompt_patches && !($result_all_autofix && !$prompt_autofix_patches)) { if (!Filesystem::pathExists($old_file)) { $old_file = '/dev/null'; } $new_file = new TempFile(); $new = $patcher->getModifiedFileContent(); Filesystem::writeFile($new_file, $new); // TODO: Improve the behavior here, make it more like // difference_render(). list(, $stdout, $stderr) = exec_manual('diff -u %s %s', $old_file, $new_file); $console->writeOut('%s', $stdout); $console->writeErr('%s', $stderr); $prompt = pht('Apply this patch to %s?', phutil_console_format('__%s__', $result->getPath())); if (!$console->confirm($prompt, $default = true)) { continue; } } $patcher->writePatchToDisk(); $wrote_to_disk = true; $file_hashes[$old_file] = md5_file($old_file); } } $postamble = $renderer->renderPostamble(); if ($tmp) { Filesystem::appendFile($tmp, $postamble); Filesystem::rename($tmp, $this->getArgument('outfile')); } else { $console->writeOut('%s', $postamble); } if ($wrote_to_disk && $this->shouldAmendChanges) { if ($this->shouldAmendWithoutPrompt || $this->shouldAmendAutofixesWithoutPrompt && $all_autofix) { $console->writeOut("<bg:yellow>** %s **</bg> %s\n", pht('LINT NOTICE'), pht('Automatically amending HEAD with lint patches.')); $amend = true; } else { $amend = $console->confirm(pht('Amend HEAD with lint patches?')); } if ($amend) { if ($repository_api instanceof ArcanistGitAPI) { // Add the changes to the index before amending $repository_api->execxLocal('add -u'); } $repository_api->amendCommit(); } else { throw new ArcanistUsageException(pht('Sort out the lint changes that were applied to the working ' . 'copy and relint.')); } } if ($this->getArgument('output') == 'json') { // NOTE: Required by save_lint.php in Phabricator. return 0; } if ($failed) { if ($failed instanceof ArcanistNoEffectException) { if ($renderer instanceof ArcanistNoneLintRenderer) { return 0; } } throw $failed; } $unresolved = array(); $has_warnings = false; $has_errors = false; foreach ($results as $result) { foreach ($result->getMessages() as $message) { if (!$message->isPatchApplied()) { if ($message->isError()) { $has_errors = true; } else { if ($message->isWarning()) { $has_warnings = true; } } $unresolved[] = $message; } } } $this->unresolvedMessages = $unresolved; $cache = $this->readScratchJSONFile('lint-cache.json'); $cached = idx($cache, $this->getCacheKey(), array()); if ($cached || $use_cache) { $stopped = $engine->getStoppedPaths(); foreach ($results as $result) { $path = $result->getPath(); if (!$use_cache) { unset($cached[$path]); continue; } $abs_path = $engine->getFilePathOnDisk($path); if (!Filesystem::pathExists($abs_path)) { continue; } $version = $result->getCacheVersion(); $cached_path = array(); if (isset($stopped[$path])) { $cached_path['stopped'] = $stopped[$path]; } $cached_path['repository_version'] = $this->getRepositoryVersion(); foreach ($result->getMessages() as $message) { $granularity = $message->getGranularity(); if ($granularity == ArcanistLinter::GRANULARITY_GLOBAL) { continue; } if (!$message->isPatchApplied()) { $cached_path[] = $message->toDictionary(); } } $hash = idx($file_hashes, $abs_path); if (!$hash) { $hash = md5_file($abs_path); } $cached[$path] = array($hash => array($version => $cached_path)); } $cache[$this->getCacheKey()] = $cached; // TODO: Garbage collection. $this->writeScratchJSONFile('lint-cache.json', $cache); } // Take the most severe lint message severity and use that // as the result code. if ($has_errors) { $result_code = self::RESULT_ERRORS; } else { if ($has_warnings) { $result_code = self::RESULT_WARNINGS; } else { $result_code = self::RESULT_OKAY; } } if (!$this->getParentWorkflow()) { if ($result_code == self::RESULT_OKAY) { $console->writeOut('%s', $renderer->renderOkayResult()); } } return $result_code; }
/** * Add variable to config file * @param string $Variable */ private static function addVariableToConfigFile($Variable) { $ConfigFile = str_replace('?>', NL, Filesystem::openFile('../data/config.php')); $ConfigFile .= self::defineAndGetConfigLinesFor($Variable); $ConfigFile .= NL . '?>'; Filesystem::writeFile('../data/config.php', $ConfigFile); }
$key = substr(md5(implode("\n", $hashes)), 0, 8); $package_map['packages'][$key] = array('name' => $name, 'symbols' => $package, 'uri' => '/res/pkg/' . $key . '/' . $name, 'type' => $type); foreach ($package as $symbol) { $package_map['reverse'][$symbol] = $key; } } ksort($runtime_map); $runtime_map = var_export($runtime_map, true); $runtime_map = preg_replace('/\\s+$/m', '', $runtime_map); $runtime_map = preg_replace('/array \\(/', 'array(', $runtime_map); $package_map['packages'] = isort($package_map['packages'], 'name'); ksort($package_map['reverse']); $package_map = var_export($package_map, true); $package_map = preg_replace('/\\s+$/m', '', $package_map); $package_map = preg_replace('/array \\(/', 'array(', $package_map); $generated = '@' . 'generated'; $resource_map = <<<EOFILE <?php /** * This file is automatically generated. Use 'celerity_mapper.php' to rebuild * it. * {$generated} */ celerity_register_resource_map({$runtime_map}, {$package_map}); EOFILE; echo "Writing map...\n"; Filesystem::writeFile($celerity_path, $resource_map); echo "Done.\n";
public function __construct(array $argv) { PhutilServiceProfiler::getInstance()->enableDiscardMode(); $original_argv = $argv; $args = new PhutilArgumentParser($argv); $args->setTagline('daemon overseer'); $args->setSynopsis(<<<EOHELP **launch_daemon.php** [__options__] __daemon__ Launch and oversee an instance of __daemon__. EOHELP ); $args->parseStandardArguments(); $args->parsePartial(array(array('name' => 'trace-memory', 'help' => 'Enable debug memory tracing.'), array('name' => 'log', 'param' => 'file', 'help' => 'Send output to __file__.'), array('name' => 'daemonize', 'help' => 'Run in the background.'), array('name' => 'phd', 'param' => 'dir', 'help' => 'Write PID information to __dir__.'), array('name' => 'verbose', 'help' => 'Enable verbose activity logging.'), array('name' => 'load-phutil-library', 'param' => 'library', 'repeat' => true, 'help' => 'Load __library__.'))); $argv = array(); $more = $args->getUnconsumedArgumentVector(); $this->daemon = array_shift($more); if (!$this->daemon) { $args->printHelpAndExit(); } if ($args->getArg('trace')) { $this->traceMode = true; $argv[] = '--trace'; } if ($args->getArg('trace-memory')) { $this->traceMode = true; $this->traceMemory = true; $argv[] = '--trace-memory'; } if ($args->getArg('load-phutil-library')) { foreach ($args->getArg('load-phutil-library') as $library) { $argv[] = '--load-phutil-library=' . $library; } } $log = $args->getArg('log'); if ($log) { ini_set('error_log', $log); $argv[] = '--log=' . $log; } $verbose = $args->getArg('verbose'); if ($verbose) { $this->verbose = true; $argv[] = '--verbose'; } $this->daemonize = $args->getArg('daemonize'); $this->phddir = $args->getArg('phd'); $this->argv = $argv; $this->moreArgs = coalesce($more, array()); error_log("Bringing daemon '{$this->daemon}' online..."); if (self::$instance) { throw new Exception('You may not instantiate more than one Overseer per process.'); } self::$instance = $this; if ($this->daemonize) { // We need to get rid of these or the daemon will hang when we TERM it // waiting for something to read the buffers. TODO: Learn how unix works. fclose(STDOUT); fclose(STDERR); ob_start(); $pid = pcntl_fork(); if ($pid === -1) { throw new Exception('Unable to fork!'); } else { if ($pid) { exit(0); } } } if ($this->phddir) { $desc = array('name' => $this->daemon, 'argv' => $this->moreArgs, 'pid' => getmypid(), 'start' => time()); Filesystem::writeFile($this->phddir . '/daemon.' . getmypid(), json_encode($desc)); } $this->daemonID = $this->generateDaemonID(); $this->dispatchEvent(self::EVENT_DID_LAUNCH, array('argv' => array_slice($original_argv, 1), 'explicitArgv' => $this->moreArgs)); declare (ticks=1); pcntl_signal(SIGUSR1, array($this, 'didReceiveKeepaliveSignal')); pcntl_signal(SIGUSR2, array($this, 'didReceiveNotifySignal')); pcntl_signal(SIGINT, array($this, 'didReceiveGracefulSignal')); pcntl_signal(SIGTERM, array($this, 'didReceiveTerminalSignal')); }
public function generateManifest($path) { $data = $this->buildManifest(); $json = new PhutilJSON(); $data = $json->encodeFormatted($data); Filesystem::writeFile($path, $data); return $this; }