protected function executeAcquireLease(DrydockResource $resource, DrydockLease $lease)
 {
     // Because preallocated resources are manually created, we should verify
     // we have all the information we need.
     PhutilTypeSpec::checkMap($resource->getAttributesForTypeSpec(array('platform', 'host', 'port', 'credential', 'path')), array('platform' => 'string', 'host' => 'string', 'port' => 'string', 'credential' => 'string', 'path' => 'string'));
     $v_platform = $resource->getAttribute('platform');
     $v_path = $resource->getAttribute('path');
     // Similar to DrydockLocalHostBlueprint, we create a folder
     // on the remote host that the lease can use.
     $lease_id = $lease->getID();
     // Can't use DIRECTORY_SEPERATOR here because that is relevant to
     // the platform we're currently running on, not the platform we are
     // remoting to.
     $separator = '/';
     if ($v_platform === 'windows') {
         $separator = '\\';
     }
     // Clean up the directory path a little.
     $base_path = rtrim($v_path, '/');
     $base_path = rtrim($base_path, '\\');
     $full_path = $base_path . $separator . $lease_id;
     $cmd = $lease->getInterface('command');
     $cmd->execx('mkdir %s', $full_path);
     $lease->setAttribute('path', $full_path);
 }
 /**
  * @task apps
  */
 public static function buildFieldList($base_class, array $spec, $object, array $options = array())
 {
     PhutilTypeSpec::checkMap($options, array('withDisabled' => 'optional bool'));
     $field_objects = id(new PhutilSymbolLoader())->setAncestorClass($base_class)->loadObjects();
     $fields = array();
     $from_map = array();
     foreach ($field_objects as $field_object) {
         $current_class = get_class($field_object);
         foreach ($field_object->createFields($object) as $field) {
             $key = $field->getFieldKey();
             if (isset($fields[$key])) {
                 throw new Exception(pht("Both '%s' and '%s' define a custom field with " . "field key '%s'. Field keys must be unique.", $from_map[$key], $current_class, $key));
             }
             $from_map[$key] = $current_class;
             $fields[$key] = $field;
         }
     }
     foreach ($fields as $key => $field) {
         if (!$field->isFieldEnabled()) {
             unset($fields[$key]);
         }
     }
     $fields = array_select_keys($fields, array_keys($spec)) + $fields;
     if (empty($options['withDisabled'])) {
         foreach ($fields as $key => $field) {
             $config = idx($spec, $key, array()) + array('disabled' => $field->shouldDisableByDefault());
             if (!empty($config['disabled'])) {
                 if ($field->canDisableField()) {
                     unset($fields[$key]);
                 }
             }
         }
     }
     return $fields;
 }
 public static function validateConfiguration($config)
 {
     if (!is_array($config)) {
         throw new Exception(pht('Configuration is not valid. Maniphest points configuration must ' . 'be a dictionary.'));
     }
     PhutilTypeSpec::checkMap($config, array('enabled' => 'optional bool', 'label' => 'optional string', 'action' => 'optional string'));
 }
 public static function newFromDictionary(HarbormasterBuildTarget $build_target, array $dict)
 {
     $obj = self::initializeNewUnitMessage($build_target);
     $spec = self::getParameterSpec();
     $spec = ipull($spec, 'type');
     // We're just going to ignore extra keys for now, to make it easier to
     // add stuff here later on.
     $dict = array_select_keys($dict, array_keys($spec));
     PhutilTypeSpec::checkMap($dict, $spec);
     $obj->setEngine(idx($dict, 'engine', ''));
     $obj->setNamespace(idx($dict, 'namespace', ''));
     $obj->setName($dict['name']);
     $obj->setResult($dict['result']);
     $obj->setDuration((double) idx($dict, 'duration'));
     $path = idx($dict, 'path');
     if (strlen($path)) {
         $obj->setProperty('path', $path);
     }
     $coverage = idx($dict, 'coverage');
     if ($coverage) {
         $obj->setProperty('coverage', $coverage);
     }
     $details = idx($dict, 'details');
     if ($details) {
         $obj->setProperty('details', $details);
     }
     return $obj;
 }
Example #5
0
 protected function readBookConfiguration($book_path)
 {
     if ($book_path === null) {
         throw new PhutilArgumentUsageException('Specify a Diviner book configuration file with --book.');
     }
     $book_data = Filesystem::readFile($book_path);
     $book = json_decode($book_data, true);
     if (!is_array($book)) {
         throw new PhutilArgumentUsageException("Book configuration '{$book_path}' is not in JSON format.");
     }
     PhutilTypeSpec::checkMap($book, array('name' => 'string', 'title' => 'optional string', 'short' => 'optional string', 'preface' => 'optional string', 'root' => 'optional string', 'uri.source' => 'optional string', 'rules' => 'optional map<regex, string>', 'exclude' => 'optional regex|list<regex>', 'groups' => 'optional map<string, map<string, wild>>'));
     // If the book specifies a "root", resolve it; otherwise, use the directory
     // the book configuration file lives in.
     $full_path = dirname(Filesystem::resolvePath($book_path));
     if (empty($book['root'])) {
         $book['root'] = '.';
     }
     $book['root'] = Filesystem::resolvePath($book['root'], $full_path);
     if (!preg_match('/^[a-z][a-z-]*\\z/', $book['name'])) {
         $name = $book['name'];
         throw new PhutilArgumentUsageException("Book configuration '{$book_path}' has name '{$name}', but book names " . "must include only lowercase letters and hyphens.");
     }
     foreach (idx($book, 'groups', array()) as $group) {
         PhutilTypeSpec::checkmap($group, array('name' => 'string', 'include' => 'optional regex|list<regex>'));
     }
     $this->bookConfigPath = $book_path;
     $this->config = $book;
 }
 public function validateOption(PhabricatorConfigOption $option, $value)
 {
     if (!is_array($value)) {
         throw new Exception(pht('Logo configuration is not valid: value must be a dictionary.'));
     }
     PhutilTypeSpec::checkMap($value, array('logoImagePHID' => 'optional string|null', 'wordmarkText' => 'optional string|null'));
 }
 public function __construct(PhutilTypeSpec $type, $value, $name = null, $err = null)
 {
     if ($name !== null) {
         $invalid = pht("Parameter '%s' has invalid type.", $name);
     } else {
         $invalid = pht("Parameter has invalid type.");
     }
     if ($type->getType() == 'regex') {
         if (is_string($value)) {
             $message = pht("Expected a regular expression, but '%s' is not valid: %s", $value, $err);
         } else {
             $message = pht("Expected a regular expression, but value is not valid: %s", $err);
         }
     } else {
         $message = pht("Expected type '%s', got type '%s'.", $type->toString(), PhutilTypeSpec::getTypeOf($value));
     }
     parent::__construct($invalid . ' ' . $message);
 }
 public function loadDictionary($path)
 {
     $root = $this->getProjectRoot();
     $path = Filesystem::resolvePath($path, $root);
     $dict = phutil_json_decode(Filesystem::readFile($path));
     PhutilTypeSpec::checkMap($dict, array('rules' => 'map<string, map<string, string>>'));
     $rules = $dict['rules'];
     $this->dictionaries[] = $path;
     $this->exactWordRules = array_merge($this->exactWordRules, idx($rules, 'exact', array()));
     $this->partialWordRules = array_merge($this->partialWordRules, idx($rules, 'partial', array()));
 }
 public function buildTestEngines()
 {
     $working_copy = $this->getWorkingCopy();
     $config_path = $working_copy->getProjectPath('.arcunit');
     if (!Filesystem::pathExists($config_path)) {
         throw new ArcanistUsageException(pht("Unable to find '%s' file to configure test engines. Create an " . "'%s' file in the root directory of the working copy.", '.arcunit', '.arcunit'));
     }
     $data = Filesystem::readFile($config_path);
     $config = null;
     try {
         $config = phutil_json_decode($data);
     } catch (PhutilJSONParserException $ex) {
         throw new PhutilProxyException(pht("Expected '%s' file to be a valid JSON file, but " . "failed to decode '%s'.", '.arcunit', $config_path), $ex);
     }
     $test_engines = $this->loadAvailableTestEngines();
     try {
         PhutilTypeSpec::checkMap($config, array('engines' => 'map<string, map<string, wild>>'));
     } catch (PhutilTypeCheckException $ex) {
         throw new PhutilProxyException(pht("Error in parsing '%s' file.", $config_path), $ex);
     }
     $built_test_engines = array();
     $all_paths = $this->getPaths();
     foreach ($config['engines'] as $name => $spec) {
         $type = idx($spec, 'type');
         if ($type !== null) {
             if (empty($test_engines[$type])) {
                 throw new ArcanistUsageException(pht("Test engine '%s' specifies invalid type '%s'. " . "Available test engines are: %s.", $name, $type, implode(', ', array_keys($test_engines))));
             }
             $test_engine = clone $test_engines[$type];
         } else {
             // We'll raise an error below about the invalid "type" key.
             // TODO: Can we just do the type check first, and simplify this a bit?
             $test_engine = null;
         }
         try {
             PhutilTypeSpec::checkMap($spec, array('type' => 'string', 'include' => 'optional regex | list<regex>', 'exclude' => 'optional regex | list<regex>'));
         } catch (PhutilTypeCheckException $ex) {
             throw new PhutilProxyException(pht("Error in parsing '%s' file, for test engine '%s'.", '.arcunit', $name), $ex);
         }
         if ($all_paths) {
             $include = (array) idx($spec, 'include', array());
             $exclude = (array) idx($spec, 'exclude', array());
             $paths = $this->matchPaths($all_paths, $include, $exclude);
             $test_engine->setPaths($paths);
         }
         $built_test_engines[] = $test_engine;
     }
     return $built_test_engines;
 }
 public function validateOption(PhabricatorConfigOption $option, $value)
 {
     if (!is_array($value)) {
         throw new Exception(pht('Keyring configuration is not valid: value must be a ' . 'list of encryption keys.'));
     }
     foreach ($value as $index => $spec) {
         if (!is_array($spec)) {
             throw new Exception(pht('Keyring configuration is not valid: each entry in the list must ' . 'be a dictionary describing an encryption key, but the value ' . 'with index "%s" is not a dictionary.', $index));
         }
     }
     $map = array();
     $defaults = array();
     foreach ($value as $index => $spec) {
         try {
             PhutilTypeSpec::checkMap($spec, array('name' => 'string', 'type' => 'string', 'material.base64' => 'string', 'default' => 'optional bool'));
         } catch (Exception $ex) {
             throw new Exception(pht('Keyring configuration has an invalid key specification (at ' . 'index "%s"): %s.', $index, $ex->getMessage()));
         }
         $name = $spec['name'];
         if (isset($map[$name])) {
             throw new Exception(pht('Keyring configuration is invalid: it describes multiple keys ' . 'with the same name ("%s"). Each key must have a unique name.', $name));
         }
         $map[$name] = true;
         if (idx($spec, 'default')) {
             $defaults[] = $name;
         }
         $type = $spec['type'];
         switch ($type) {
             case 'aes-256-cbc':
                 if (!function_exists('openssl_encrypt')) {
                     throw new Exception(pht('Keyring is configured with a "%s" key, but the PHP OpenSSL ' . 'extension is not installed. Install the OpenSSL extension ' . 'to enable encryption.', $type));
                 }
                 $material = $spec['material.base64'];
                 $material = base64_decode($material, true);
                 if ($material === false) {
                     throw new Exception(pht('Keyring specifies an invalid key ("%s"): key material ' . 'should be base64 encoded.', $name));
                 }
                 if (strlen($material) != 32) {
                     throw new Exception(pht('Keyring specifies an invalid key ("%s"): key material ' . 'should be 32 bytes (256 bits) but has length %s.', $name, new PhutilNumber(strlen($material))));
                 }
                 break;
             default:
                 throw new Exception(pht('Keyring configuration is invalid: it describes a key with ' . 'type "%s", but this type is unknown.', $type));
         }
     }
     if (count($defaults) > 1) {
         throw new Exception(pht('Keyring configuration is invalid: it describes multiple default ' . 'encryption keys. No more than one key may be the default key. ' . 'Keys currently configured as defaults: %s.', implode(', ', $defaults)));
     }
 }
 public function validateTransactions($object, array $xactions)
 {
     $errors = array();
     if (!$xactions) {
         return $errors;
     }
     $old = mpull($object->getPaths(), 'getRef');
     foreach ($xactions as $xaction) {
         $new = $xaction->getNewValue();
         // Check that we have a list of paths.
         if (!is_array($new)) {
             $errors[] = $this->newInvalidError(pht('Path specification must be a list of paths.'), $xaction);
             continue;
         }
         // Check that each item in the list is formatted properly.
         $type_exception = null;
         foreach ($new as $key => $value) {
             try {
                 PhutilTypeSpec::checkMap($value, array('repositoryPHID' => 'string', 'path' => 'string', 'excluded' => 'optional wild'));
             } catch (PhutilTypeCheckException $ex) {
                 $errors[] = $this->newInvalidError(pht('Path specification list contains invalid value ' . 'in key "%s": %s.', $key, $ex->getMessage()), $xaction);
                 $type_exception = $ex;
             }
         }
         if ($type_exception) {
             continue;
         }
         // Check that any new paths reference legitimate repositories which
         // the viewer has permission to see.
         list($rem, $add) = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);
         if ($add) {
             $repository_phids = ipull($add, 'repositoryPHID');
             $repositories = id(new PhabricatorRepositoryQuery())->setViewer($this->getActor())->withPHIDs($repository_phids)->execute();
             $repositories = mpull($repositories, null, 'getPHID');
             foreach ($add as $ref) {
                 $repository_phid = $ref['repositoryPHID'];
                 if (isset($repositories[$repository_phid])) {
                     continue;
                 }
                 $errors[] = $this->newInvalidError(pht('Path specification list references repository PHID "%s", ' . 'but that is not a valid, visible repository.', $repository_phid));
             }
         }
     }
     return $errors;
 }
 public function validateOption(PhabricatorConfigOption $option, $value)
 {
     if (!is_array($value)) {
         throw new Exception(pht('Database cluster configuration is not valid: value must be a ' . 'list of database hosts.'));
     }
     foreach ($value as $index => $spec) {
         if (!is_array($spec)) {
             throw new Exception(pht('Database cluster configuration is not valid: each entry in the ' . 'list must be a dictionary describing a database host, but ' . 'the value with index "%s" is not a dictionary.', $index));
         }
     }
     $masters = array();
     $map = array();
     foreach ($value as $index => $spec) {
         try {
             PhutilTypeSpec::checkMap($spec, array('host' => 'string', 'role' => 'string', 'port' => 'optional int', 'user' => 'optional string', 'pass' => 'optional string', 'disabled' => 'optional bool'));
         } catch (Exception $ex) {
             throw new Exception(pht('Database cluster configuration has an invalid host ' . 'specification (at index "%s"): %s.', $index, $ex->getMessage()));
         }
         $role = $spec['role'];
         $host = $spec['host'];
         $port = idx($spec, 'port');
         switch ($role) {
             case 'master':
             case 'replica':
                 break;
             default:
                 throw new Exception(pht('Database cluster configuration describes an invalid ' . 'host ("%s", at index "%s") with an unrecognized role ("%s"). ' . 'Valid roles are "%s" or "%s".', $spec['host'], $index, $spec['role'], 'master', 'replica'));
         }
         if ($role === 'master') {
             $masters[] = $host;
         }
         // We can't guarantee that you didn't just give the same host two
         // different names in DNS, but this check can catch silly copy/paste
         // mistakes.
         $key = "{$host}:{$port}";
         if (isset($map[$key])) {
             throw new Exception(pht('Database cluster configuration is invalid: it describes the ' . 'same host ("%s") multiple times. Each host should appear only ' . 'once in the list.', $host));
         }
         $map[$key] = true;
     }
     if (count($masters) > 1) {
         throw new Exception(pht('Database cluster configuration is invalid: it describes multiple ' . 'masters. No more than one host may be a master. Hosts currently ' . 'configured as masters: %s.', implode(', ', $masters)));
     }
 }
 public static function newFromDictionary(HarbormasterBuildTarget $build_target, array $dict)
 {
     $obj = self::initializeNewLintMessage($build_target);
     $spec = array('path' => 'string', 'line' => 'optional int', 'char' => 'optional int', 'code' => 'string', 'severity' => 'string', 'name' => 'string', 'description' => 'optional string');
     // We're just going to ignore extra keys for now, to make it easier to
     // add stuff here later on.
     $dict = array_select_keys($dict, array_keys($spec));
     PhutilTypeSpec::checkMap($dict, $spec);
     $obj->setPath($dict['path']);
     $obj->setLine(idx($dict, 'line'));
     $obj->setCharacterOffset(idx($dict, 'char'));
     $obj->setCode($dict['code']);
     $obj->setSeverity($dict['severity']);
     $obj->setName($dict['name']);
     $description = idx($dict, 'description');
     if (strlen($description)) {
         $obj->setProperty('description', $description);
     }
     return $obj;
 }
 public function execute(PhutilArgumentParser $args)
 {
     $viewer = $this->getViewer();
     echo tsprintf("%s\n", pht('Reading list of hints from stdin...'));
     $hints = file_get_contents('php://stdin');
     if ($hints === false) {
         throw new PhutilArgumentUsageException(pht('Failed to read stdin.'));
     }
     try {
         $hints = phutil_json_decode($hints);
     } catch (Exception $ex) {
         throw new PhutilArgumentUsageException(pht('Expected a list of hints in JSON format: %s', $ex->getMessage()));
     }
     $repositories = array();
     foreach ($hints as $idx => $hint) {
         if (!is_array($hint)) {
             throw new PhutilArgumentUsageException(pht('Each item in the list of hints should be a JSON object, but ' . 'the item at index "%s" is not.', $idx));
         }
         try {
             PhutilTypeSpec::checkMap($hint, array('repository' => 'string|int', 'old' => 'string', 'new' => 'optional string|null', 'hint' => 'string'));
         } catch (Exception $ex) {
             throw new PhutilArgumentUsageException(pht('Unexpected hint format at index "%s": %s', $idx, $ex->getMessage()));
         }
         $repository_identifier = $hint['repository'];
         $repository = idx($repositories, $repository_identifier);
         if (!$repository) {
             $repository = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withIdentifiers(array($repository_identifier))->executeOne();
             if (!$repository) {
                 throw new PhutilArgumentUsageException(pht('Repository identifier "%s" (in hint at index "%s") does not ' . 'identify a valid repository.', $repository_identifier, $idx));
             }
             $repositories[$repository_identifier] = $repository;
         }
         PhabricatorRepositoryCommitHint::updateHint($repository->getPHID(), $hint['old'], idx($hint, 'new'), $hint['hint']);
         echo tsprintf("%s\n", pht('Updated hint for "%s".', $hint['old']));
     }
 }
 public function validateArtifactData(array $artifact_data)
 {
     $artifact_spec = $this->getArtifactParameterSpecification();
     PhutilTypeSpec::checkMap($artifact_data, $artifact_spec);
 }
 public function validateProperties(array $properties)
 {
     PhutilTypeSpec::checkMap($properties, array('epoch' => 'int'));
 }
 public function addColumn($key, array $column)
 {
     PhutilTypeSpec::checkMap($column, array('title' => 'string', 'align' => 'optional string'));
     $this->columns[$key] = $column;
     return $this;
 }
 private function buildMoveTransaction(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     $new = $xaction->getNewValue();
     if (!is_array($new)) {
         $this->validateColumnPHID($new);
         $new = array($new);
     }
     $nearby_phids = array();
     foreach ($new as $key => $value) {
         if (!is_array($value)) {
             $this->validateColumnPHID($value);
             $value = array('columnPHID' => $value);
         }
         PhutilTypeSpec::checkMap($value, array('columnPHID' => 'string', 'beforePHID' => 'optional string', 'afterPHID' => 'optional string'));
         $new[$key] = $value;
         if (!empty($value['beforePHID'])) {
             $nearby_phids[] = $value['beforePHID'];
         }
         if (!empty($value['afterPHID'])) {
             $nearby_phids[] = $value['afterPHID'];
         }
     }
     if ($nearby_phids) {
         $nearby_objects = id(new PhabricatorObjectQuery())->setViewer($this->getActor())->withPHIDs($nearby_phids)->execute();
         $nearby_objects = mpull($nearby_objects, null, 'getPHID');
     } else {
         $nearby_objects = array();
     }
     $column_phids = ipull($new, 'columnPHID');
     if ($column_phids) {
         $columns = id(new PhabricatorProjectColumnQuery())->setViewer($this->getActor())->withPHIDs($column_phids)->execute();
         $columns = mpull($columns, null, 'getPHID');
     } else {
         $columns = array();
     }
     $board_phids = mpull($columns, 'getProjectPHID');
     $object_phid = $object->getPHID();
     $object_phids = $nearby_phids;
     // Note that we may not have an object PHID if we're creating a new
     // object.
     if ($object_phid) {
         $object_phids[] = $object_phid;
     }
     if ($object_phids) {
         $layout_engine = id(new PhabricatorBoardLayoutEngine())->setViewer($this->getActor())->setBoardPHIDs($board_phids)->setObjectPHIDs($object_phids)->setFetchAllBoards(true)->executeLayout();
     }
     foreach ($new as $key => $spec) {
         $column_phid = $spec['columnPHID'];
         $column = idx($columns, $column_phid);
         if (!$column) {
             throw new Exception(pht('Column move transaction specifies column PHID "%s", but there ' . 'is no corresponding column with this PHID.', $column_phid));
         }
         $board_phid = $column->getProjectPHID();
         $nearby = array();
         if (!empty($spec['beforePHID'])) {
             $nearby['beforePHID'] = $spec['beforePHID'];
         }
         if (!empty($spec['afterPHID'])) {
             $nearby['afterPHID'] = $spec['afterPHID'];
         }
         if (count($nearby) > 1) {
             throw new Exception(pht('Column move transaction moves object to multiple positions. ' . 'Specify only "beforePHID" or "afterPHID", not both.'));
         }
         foreach ($nearby as $where => $nearby_phid) {
             if (empty($nearby_objects[$nearby_phid])) {
                 throw new Exception(pht('Column move transaction specifies object "%s" as "%s", but ' . 'there is no corresponding object with this PHID.', $object_phid, $where));
             }
             $nearby_columns = $layout_engine->getObjectColumns($board_phid, $nearby_phid);
             $nearby_columns = mpull($nearby_columns, null, 'getPHID');
             if (empty($nearby_columns[$column_phid])) {
                 throw new Exception(pht('Column move transaction specifies object "%s" as "%s" in ' . 'column "%s", but this object is not in that column!', $nearby_phid, $where, $column_phid));
             }
         }
         if ($object_phid) {
             $old_columns = $layout_engine->getObjectColumns($board_phid, $object_phid);
             $old_column_phids = mpull($old_columns, 'getPHID');
         } else {
             $old_column_phids = array();
         }
         $spec += array('boardPHID' => $board_phid, 'fromColumnPHIDs' => $old_column_phids);
         // Check if the object is already in this column, and isn't being moved.
         // We can just drop this column change if it has no effect.
         $from_map = array_fuse($spec['fromColumnPHIDs']);
         $already_here = isset($from_map[$column_phid]);
         $is_reordering = (bool) $nearby;
         if ($already_here && !$is_reordering) {
             unset($new[$key]);
         } else {
             $new[$key] = $spec;
         }
     }
     $new = array_values($new);
     $xaction->setNewValue($new);
     $more = array();
     // If we're moving the object into a column and it does not already belong
     // in the column, add the appropriate board. For normal columns, this
     // is the board PHID. For proxy columns, it is the proxy PHID, unless the
     // object is already a member of some descendant of the proxy PHID.
     // The major case where this can happen is moves via the API, but it also
     // happens when a user drags a task from the "Backlog" to a milestone
     // column.
     if ($object_phid) {
         $current_phids = PhabricatorEdgeQuery::loadDestinationPHIDs($object_phid, PhabricatorProjectObjectHasProjectEdgeType::EDGECONST);
         $current_phids = array_fuse($current_phids);
     } else {
         $current_phids = array();
     }
     $add_boards = array();
     foreach ($new as $move) {
         $column_phid = $move['columnPHID'];
         $board_phid = $move['boardPHID'];
         $column = $columns[$column_phid];
         $proxy_phid = $column->getProxyPHID();
         // If this is a normal column, add the board if the object isn't already
         // associated.
         if (!$proxy_phid) {
             if (!isset($current_phids[$board_phid])) {
                 $add_boards[] = $board_phid;
             }
             continue;
         }
         // If this is a proxy column but the object is already associated with
         // the proxy board, we don't need to do anything.
         if (isset($current_phids[$proxy_phid])) {
             continue;
         }
         // If this a proxy column and the object is already associated with some
         // descendant of the proxy board, we also don't need to do anything.
         $descendants = id(new PhabricatorProjectQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withAncestorProjectPHIDs(array($proxy_phid))->execute();
         $found_descendant = false;
         foreach ($descendants as $descendant) {
             if (isset($current_phids[$descendant->getPHID()])) {
                 $found_descendant = true;
                 break;
             }
         }
         if ($found_descendant) {
             continue;
         }
         // Otherwise, we're moving the object to a proxy column which it is not
         // a member of yet, so add an association to the column's proxy board.
         $add_boards[] = $proxy_phid;
     }
     if ($add_boards) {
         $more[] = id(new ManiphestTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDGE)->setMetadataValue('edge:type', PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)->setIgnoreOnNoEffect(true)->setNewValue(array('+' => array_fuse($add_boards)));
     }
     return $more;
 }
 /**
  * Simplifies the task of constructing a paging clause across multiple
  * columns. In the general case, this looks like:
  *
  *   A > a OR (A = a AND B > b) OR (A = a AND B = b AND C > c)
  *
  * To build a clause, specify the name, type, and value of each column
  * to include:
  *
  *   $this->buildPagingClauseFromMultipleColumns(
  *     $conn_r,
  *     array(
  *       array(
  *         'table' => 't',
  *         'column' => 'title',
  *         'type' => 'string',
  *         'value' => $cursor->getTitle(),
  *         'reverse' => true,
  *       ),
  *       array(
  *         'table' => 't',
  *         'column' => 'id',
  *         'type' => 'int',
  *         'value' => $cursor->getID(),
  *       ),
  *     ),
  *     array(
  *       'reversed' => $is_reversed,
  *     ));
  *
  * This method will then return a composable clause for inclusion in WHERE.
  *
  * @param AphrontDatabaseConnection Connection query will execute on.
  * @param list<map> Column description dictionaries.
  * @param map Additional constuction options.
  * @return string Query clause.
  * @task paging
  */
 protected final function buildPagingClauseFromMultipleColumns(AphrontDatabaseConnection $conn, array $columns, array $options)
 {
     foreach ($columns as $column) {
         PhutilTypeSpec::checkMap($column, array('table' => 'optional string|null', 'column' => 'string', 'value' => 'wild', 'type' => 'string', 'reverse' => 'optional bool', 'unique' => 'optional bool', 'null' => 'optional string|null'));
     }
     PhutilTypeSpec::checkMap($options, array('reversed' => 'optional bool'));
     $is_query_reversed = idx($options, 'reversed', false);
     $clauses = array();
     $accumulated = array();
     $last_key = last_key($columns);
     foreach ($columns as $key => $column) {
         $type = $column['type'];
         $null = idx($column, 'null');
         if ($column['value'] === null) {
             if ($null) {
                 $value = null;
             } else {
                 throw new Exception(pht('Column "%s" has null value, but does not specify a null ' . 'behavior.', $key));
             }
         } else {
             switch ($type) {
                 case 'int':
                     $value = qsprintf($conn, '%d', $column['value']);
                     break;
                 case 'float':
                     $value = qsprintf($conn, '%f', $column['value']);
                     break;
                 case 'string':
                     $value = qsprintf($conn, '%s', $column['value']);
                     break;
                 default:
                     throw new Exception(pht('Column "%s" has unknown column type "%s".', $column['column'], $type));
             }
         }
         $is_column_reversed = idx($column, 'reverse', false);
         $reverse = ($is_query_reversed xor $is_column_reversed);
         $clause = $accumulated;
         $table_name = idx($column, 'table');
         $column_name = $column['column'];
         if ($table_name !== null) {
             $field = qsprintf($conn, '%T.%T', $table_name, $column_name);
         } else {
             $field = qsprintf($conn, '%T', $column_name);
         }
         $parts = array();
         if ($null) {
             $can_page_if_null = $null === 'head';
             $can_page_if_nonnull = $null === 'tail';
             if ($reverse) {
                 $can_page_if_null = !$can_page_if_null;
                 $can_page_if_nonnull = !$can_page_if_nonnull;
             }
             $subclause = null;
             if ($can_page_if_null && $value === null) {
                 $parts[] = qsprintf($conn, '(%Q IS NOT NULL)', $field);
             } else {
                 if ($can_page_if_nonnull && $value !== null) {
                     $parts[] = qsprintf($conn, '(%Q IS NULL)', $field);
                 }
             }
         }
         if ($value !== null) {
             $parts[] = qsprintf($conn, '%Q %Q %Q', $field, $reverse ? '>' : '<', $value);
         }
         if ($parts) {
             if (count($parts) > 1) {
                 $clause[] = '(' . implode(') OR (', $parts) . ')';
             } else {
                 $clause[] = head($parts);
             }
         }
         if ($clause) {
             if (count($clause) > 1) {
                 $clauses[] = '(' . implode(') AND (', $clause) . ')';
             } else {
                 $clauses[] = head($clause);
             }
         }
         if ($value === null) {
             $accumulated[] = qsprintf($conn, '%Q IS NULL', $field);
         } else {
             $accumulated[] = qsprintf($conn, '%Q = %Q', $field, $value);
         }
     }
     return '(' . implode(') OR (', $clauses) . ')';
 }
 public function getValueForStorage($value)
 {
     PhutilTypeSpec::newFromString('list<string>')->check($value);
     return array_values($value);
 }
 public static final function scheduleTask($task_class, $data, $options = array())
 {
     PhutilTypeSpec::checkMap($options, array('priority' => 'optional int|null', 'objectPHID' => 'optional string|null', 'delayUntil' => 'optional int|null'));
     $priority = idx($options, 'priority');
     if ($priority === null) {
         $priority = self::PRIORITY_DEFAULT;
     }
     $object_phid = idx($options, 'objectPHID');
     $task = id(new PhabricatorWorkerActiveTask())->setTaskClass($task_class)->setData($data)->setPriority($priority)->setObjectPHID($object_phid);
     $delay = idx($options, 'delayUntil');
     if ($delay) {
         $task->setLeaseExpires($delay);
     }
     if (self::$runAllTasksInProcess) {
         // Do the work in-process.
         $worker = newv($task_class, array($data));
         while (true) {
             try {
                 $worker->doWork();
                 foreach ($worker->getQueuedTasks() as $queued_task) {
                     list($queued_class, $queued_data, $queued_priority) = $queued_task;
                     $queued_options = array('priority' => $queued_priority);
                     self::scheduleTask($queued_class, $queued_data, $queued_options);
                 }
                 break;
             } catch (PhabricatorWorkerYieldException $ex) {
                 phlog(pht('In-process task "%s" yielded for %s seconds, sleeping...', $task_class, $ex->getDuration()));
                 sleep($ex->getDuration());
             }
         }
         // Now, save a task row and immediately archive it so we can return an
         // object with a valid ID.
         $task->openTransaction();
         $task->save();
         $archived = $task->archiveTask(PhabricatorWorkerArchiveTask::RESULT_SUCCESS, 0);
         $task->saveTransaction();
         return $archived;
     } else {
         $task->save();
         return $task;
     }
 }
 protected final function executeStartCommand(array $options)
 {
     PhutilTypeSpec::checkMap($options, array('keep-leases' => 'optional bool', 'force' => 'optional bool', 'reserve' => 'optional float'));
     $console = PhutilConsole::getConsole();
     if (!idx($options, 'force')) {
         $running = $this->loadRunningDaemons();
         // This may include daemons which were launched but which are no longer
         // running; check that we actually have active daemons before failing.
         foreach ($running as $daemon) {
             if ($daemon->isRunning()) {
                 $message = pht("phd start: Unable to start daemons because daemons are already " . "running.\n\n" . "You can view running daemons with '%s'.\n" . "You can stop running daemons with '%s'.\n" . "You can use '%s' to stop all daemons before starting " . "new daemons.\n" . "You can force daemons to start anyway with %s.", 'phd status', 'phd stop', 'phd restart', '--force');
                 $console->writeErr("%s\n", $message);
                 exit(1);
             }
         }
     }
     if (idx($options, 'keep-leases')) {
         $console->writeErr("%s\n", pht('Not touching active task queue leases.'));
     } else {
         $console->writeErr("%s\n", pht('Freeing active task leases...'));
         $count = $this->freeActiveLeases();
         $console->writeErr("%s\n", pht('Freed %s task lease(s).', new PhutilNumber($count)));
     }
     $daemons = array(array('class' => 'PhabricatorRepositoryPullLocalDaemon'), array('class' => 'PhabricatorTriggerDaemon'), array('class' => 'PhabricatorTaskmasterDaemon', 'autoscale' => array('group' => 'task', 'pool' => PhabricatorEnv::getEnvConfig('phd.taskmasters'), 'reserve' => idx($options, 'reserve', 0))));
     $this->launchDaemons($daemons, $is_debug = false);
     $console->writeErr("%s\n", pht('Done.'));
     return 0;
 }
 /**
  * Render a standard login/register button element.
  *
  * The `$attributes` parameter takes these keys:
  *
  *   - `uri`: URI the button should take the user to when clicked.
  *   - `method`: Optional HTTP method the button should use, defaults to GET.
  *
  * @param   AphrontRequest  HTTP request.
  * @param   string          Request mode string.
  * @param   map             Additional parameters, see above.
  * @return  wild            Login button.
  */
 protected function renderStandardLoginButton(AphrontRequest $request, $mode, array $attributes = array())
 {
     PhutilTypeSpec::checkMap($attributes, array('method' => 'optional string', 'uri' => 'string', 'sigil' => 'optional string'));
     $viewer = $request->getUser();
     $adapter = $this->getAdapter();
     if ($mode == 'link') {
         $button_text = pht('Link External Account');
     } else {
         if ($mode == 'refresh') {
             $button_text = pht('Refresh Account Link');
         } else {
             if ($mode == 'invite') {
                 $button_text = pht('Register Account');
             } else {
                 if ($this->shouldAllowRegistration()) {
                     $button_text = pht('Login or Register');
                 } else {
                     $button_text = pht('Login');
                 }
             }
         }
     }
     $icon = id(new PHUIIconView())->setSpriteSheet(PHUIIconView::SPRITE_LOGIN)->setSpriteIcon($this->getLoginIcon());
     $button = id(new PHUIButtonView())->setSize(PHUIButtonView::BIG)->setColor(PHUIButtonView::GREY)->setIcon($icon)->setText($button_text)->setSubtext($this->getProviderName());
     $uri = $attributes['uri'];
     $uri = new PhutilURI($uri);
     $params = $uri->getQueryParams();
     $uri->setQueryParams(array());
     $content = array($button);
     foreach ($params as $key => $value) {
         $content[] = phutil_tag('input', array('type' => 'hidden', 'name' => $key, 'value' => $value));
     }
     return phabricator_form($viewer, array('method' => idx($attributes, 'method', 'GET'), 'action' => (string) $uri, 'sigil' => idx($attributes, 'sigil')), $content);
 }
 public function validateProperties(array $properties)
 {
     PhutilTypeSpec::checkMap($properties, array('message' => 'string'));
 }
Example #25
0
 /**
  * @task validate
  */
 public static function validateConfiguration(array $config)
 {
     foreach ($config as $key => $value) {
         if (!self::isValidStatusConstant($key)) {
             throw new Exception(pht('Key "%s" is not a valid status constant. Status constants must ' . 'be 1-12 characters long and contain only lowercase letters (a-z) ' . 'and digits (0-9). For example, "%s" or "%s" are reasonable ' . 'choices.', $key, 'open', 'closed'));
         }
         if (!is_array($value)) {
             throw new Exception(pht('Value for key "%s" should be a dictionary.', $key));
         }
         PhutilTypeSpec::checkMap($value, array('name' => 'string', 'name.full' => 'optional string', 'name.action' => 'optional string', 'closed' => 'optional bool', 'special' => 'optional string', 'transaction.icon' => 'optional string', 'transaction.color' => 'optional string', 'silly' => 'optional bool', 'prefixes' => 'optional list<string>', 'suffixes' => 'optional list<string>', 'keywords' => 'optional list<string>', 'disabled' => 'optional bool', 'claim' => 'optional bool'));
     }
     $special_map = array();
     foreach ($config as $key => $value) {
         $special = idx($value, 'special');
         if (!$special) {
             continue;
         }
         if (isset($special_map[$special])) {
             throw new Exception(pht('Configuration has two statuses both marked with the special ' . 'attribute "%s" ("%s" and "%s"). There should be only one.', $special, $special_map[$special], $key));
         }
         switch ($special) {
             case self::SPECIAL_DEFAULT:
                 if (!empty($value['closed'])) {
                     throw new Exception(pht('Status "%s" is marked as default, but it is a closed ' . 'status. The default status should be an open status.', $key));
                 }
                 break;
             case self::SPECIAL_CLOSED:
                 if (empty($value['closed'])) {
                     throw new Exception(pht('Status "%s" is marked as the default status for closing ' . 'tasks, but is not a closed status. It should be a closed ' . 'status.', $key));
                 }
                 break;
             case self::SPECIAL_DUPLICATE:
                 if (empty($value['closed'])) {
                     throw new Exception(pht('Status "%s" is marked as the status for closing tasks as ' . 'duplicates, but it is not a closed status. It should ' . 'be a closed status.', $key));
                 }
                 break;
         }
         $special_map[$special] = $key;
     }
     // NOTE: We're not explicitly validating that we have at least one open
     // and one closed status, because the DEFAULT and CLOSED specials imply
     // that to be true. If those change in the future, that might become a
     // reasonable thing to validate.
     $required = array(self::SPECIAL_DEFAULT, self::SPECIAL_CLOSED, self::SPECIAL_DUPLICATE);
     foreach ($required as $required_special) {
         if (!isset($special_map[$required_special])) {
             throw new Exception(pht('Configuration defines no task status with special attribute ' . '"%s", but you must specify a status which fills this special ' . 'role.', $required_special));
         }
     }
 }
Example #26
0
 /**
  * Configure autoscaling for this daemon.
  *
  * @param map<string, wild> Map of autoscale properties.
  * @return this
  * @task autoscale
  */
 public function setAutoscaleProperties(array $autoscale_properties)
 {
     PhutilTypeSpec::checkMap($autoscale_properties, array('group' => 'optional string', 'up' => 'optional int', 'down' => 'optional int', 'pool' => 'optional int', 'clone' => 'optional bool', 'reserve' => 'optional int|float'));
     $this->autoscaleProperties = $autoscale_properties;
     return $this;
 }
 public function buildLinters()
 {
     $working_copy = $this->getWorkingCopy();
     $config_path = $working_copy->getProjectPath('.arclint');
     if (!Filesystem::pathExists($config_path)) {
         throw new ArcanistUsageException(pht("Unable to find '%s' file to configure linters. Create an " . "'%s' file in the root directory of the working copy.", '.arclint', '.arclint'));
     }
     $data = Filesystem::readFile($config_path);
     $config = null;
     try {
         $config = phutil_json_decode($data);
     } catch (PhutilJSONParserException $ex) {
         throw new PhutilProxyException(pht("Expected '%s' file to be a valid JSON file, but " . "failed to decode '%s'.", '.arclint', $config_path), $ex);
     }
     $linters = $this->loadAvailableLinters();
     try {
         PhutilTypeSpec::checkMap($config, array('exclude' => 'optional regex | list<regex>', 'linters' => 'map<string, map<string, wild>>'));
     } catch (PhutilTypeCheckException $ex) {
         throw new PhutilProxyException(pht("Error in parsing '%s' file.", $config_path), $ex);
     }
     $global_exclude = (array) idx($config, 'exclude', array());
     $built_linters = array();
     $all_paths = $this->getPaths();
     foreach ($config['linters'] as $name => $spec) {
         $type = idx($spec, 'type');
         if ($type !== null) {
             if (empty($linters[$type])) {
                 throw new ArcanistUsageException(pht("Linter '%s' specifies invalid type '%s'. " . "Available linters are: %s.", $name, $type, implode(', ', array_keys($linters))));
             }
             $linter = clone $linters[$type];
             $linter->setEngine($this);
             $more = $linter->getLinterConfigurationOptions();
             foreach ($more as $key => $option_spec) {
                 PhutilTypeSpec::checkMap($option_spec, array('type' => 'string', 'help' => 'string'));
                 $more[$key] = $option_spec['type'];
             }
         } else {
             // We'll raise an error below about the invalid "type" key.
             $linter = null;
             $more = array();
         }
         try {
             PhutilTypeSpec::checkMap($spec, array('type' => 'string', 'include' => 'optional regex | list<regex>', 'exclude' => 'optional regex | list<regex>') + $more);
         } catch (PhutilTypeCheckException $ex) {
             throw new PhutilProxyException(pht("Error in parsing '%s' file, for linter '%s'.", '.arclint', $name), $ex);
         }
         foreach ($more as $key => $value) {
             if (array_key_exists($key, $spec)) {
                 try {
                     $linter->setLinterConfigurationValue($key, $spec[$key]);
                 } catch (Exception $ex) {
                     throw new PhutilProxyException(pht("Error in parsing '%s' file, in key '%s' for linter '%s'.", '.arclint', $key, $name), $ex);
                 }
             }
         }
         $include = (array) idx($spec, 'include', array());
         $exclude = (array) idx($spec, 'exclude', array());
         $console = PhutilConsole::getConsole();
         $console->writeLog("%s\n", pht("Examining paths for linter '%s'.", $name));
         $paths = $this->matchPaths($all_paths, $include, $exclude, $global_exclude);
         $console->writeLog("%s\n", pht("Found %d matching paths for linter '%s'.", count($paths), $name));
         $linter->setPaths($paths);
         $built_linters[] = $linter;
     }
     return $built_linters;
 }
 public function testMixedVector()
 {
     // This is a test case for an issue where we would not infer the type
     // of a vector containing a mixture of scalar and nonscalar elements
     // correctly.
     $caught = null;
     try {
         PhutilTypeSpec::checkMap(array('key' => array('!', (object) array())), array('key' => 'list<X>'));
     } catch (PhutilTypeCheckException $ex) {
         $caught = $ex;
     }
     $this->assertTrue($caught instanceof PhutilTypeCheckException);
 }
 public function testScalarOrListRegexp()
 {
     PhutilTypeSpec::checkMap(array('regex' => '/.*/'), array('regex' => 'regex | list<regex>'));
     PhutilTypeSpec::checkMap(array('regex' => array('/.*/')), array('regex' => 'regex | list<regex>'));
     PhutilTypeSpec::checkMap(array('regex' => '/.*/'), array('regex' => 'list<regex> | regex'));
     PhutilTypeSpec::checkMap(array('regex' => array('/.*/')), array('regex' => 'list<regex> | regex'));
     $this->assertTrue(true);
 }
$args->setTagline(pht('daemon executor'));
$args->setSynopsis(<<<EOHELP
**exec_daemon.php** [__options__] __daemon__ ...
    Run an instance of __daemon__.
EOHELP
);
$args->parse(array(array('name' => 'trace', 'help' => pht('Enable debug tracing.')), array('name' => 'trace-memory', 'help' => pht('Enable debug memory tracing.')), array('name' => 'verbose', 'help' => pht('Enable verbose activity logging.')), array('name' => 'label', 'short' => 'l', 'param' => 'label', 'help' => pht('Optional process label. Makes "%s" nicer, no behavioral effects.', 'ps')), array('name' => 'daemon', 'wildcard' => true)));
$trace_memory = $args->getArg('trace-memory');
$trace_mode = $args->getArg('trace') || $trace_memory;
$verbose = $args->getArg('verbose');
if (function_exists('posix_isatty') && posix_isatty(STDIN)) {
    fprintf(STDERR, pht('Reading daemon configuration from stdin...') . "\n");
}
$config = @file_get_contents('php://stdin');
$config = id(new PhutilJSONParser())->parse($config);
PhutilTypeSpec::checkMap($config, array('log' => 'optional string|null', 'argv' => 'optional list<wild>', 'load' => 'optional list<string>', 'autoscale' => 'optional wild'));
$log = idx($config, 'log');
if ($log) {
    ini_set('error_log', $log);
    PhutilErrorHandler::setErrorListener(array('PhutilDaemon', 'errorListener'));
}
$load = idx($config, 'load', array());
foreach ($load as $library) {
    $library = Filesystem::resolvePath($library);
    phutil_load_library($library);
}
PhutilErrorHandler::initialize();
$daemon = $args->getArg('daemon');
if (!$daemon) {
    throw new PhutilArgumentUsageException(pht('Specify which class of daemon to start.'));
} else {