1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 08:12:40 +01:00

Promote 2023.49 to Stable

This commit is contained in:
Aviv Eyal 2023-12-08 18:27:22 +02:00
commit ca72430916
19 changed files with 223 additions and 62 deletions

View file

@ -6,7 +6,7 @@
*
* This class takes over the PHP error and exception handlers when you call
* ##PhutilErrorHandler::initialize()## and forwards all debugging information
* to a listener you install with ##PhutilErrorHandler::setErrorListener()##.
* to a listener you install with ##PhutilErrorHandler::addErrorListener()##.
*
* To use PhutilErrorHandler, which will enhance the messages printed to the
* PHP error log, just initialize it:
@ -16,7 +16,7 @@
* To additionally install a custom listener which can print error information
* to some other file or console, register a listener:
*
* PhutilErrorHandler::setErrorListener($some_callback);
* PhutilErrorHandler::addErrorListener($some_callback);
*
* For information on writing an error listener, see
* @{function:phutil_error_listener_example}. Providing a listener is optional,
@ -31,7 +31,7 @@
*/
final class PhutilErrorHandler extends Phobject {
private static $errorListener = null;
private static $errorListeners = array();
private static $initialized = false;
private static $traps = array();
@ -68,8 +68,15 @@ final class PhutilErrorHandler extends Phobject {
* @return void
* @task config
*/
public static function addErrorListener($listener) {
self::$errorListeners[] = $listener;
}
/**
* Deprecated - use `addErrorListener`.
*/
public static function setErrorListener($listener) {
self::$errorListener = $listener;
self::addErrorListener($listener);
}
@ -203,12 +210,17 @@ final class PhutilErrorHandler extends Phobject {
if (($num === E_USER_ERROR) ||
($num === E_USER_WARNING) ||
($num === E_USER_NOTICE)) {
($num === E_USER_NOTICE) ||
($num === E_DEPRECATED)) {
// See T15554 - we special-case E_DEPRECATED because we don't want them
// to kill the process.
$level = ($num === E_DEPRECATED) ? self::DEPRECATED : self::ERROR;
$trace = debug_backtrace();
array_shift($trace);
self::dispatchErrorMessage(
self::ERROR,
$level,
$str,
array(
'file' => $file,
@ -380,6 +392,7 @@ final class PhutilErrorHandler extends Phobject {
$timestamp = date('Y-m-d H:i:s');
switch ($event) {
case self::DEPRECATED:
case self::ERROR:
$default_message = sprintf(
'[%s] ERROR %d: %s at [%s:%d]',
@ -432,7 +445,7 @@ final class PhutilErrorHandler extends Phobject {
break;
}
if (self::$errorListener) {
if (self::$errorListeners) {
static $handling_error;
if ($handling_error) {
error_log(
@ -441,7 +454,9 @@ final class PhutilErrorHandler extends Phobject {
return;
}
$handling_error = true;
call_user_func(self::$errorListener, $event, $value, $metadata);
foreach (self::$errorListeners as $error_listener) {
call_user_func($error_listener, $event, $value, $metadata);
}
$handling_error = false;
}
}

View file

@ -41,7 +41,7 @@ function phlog($value/* , ... */) {
/**
* Example @{class:PhutilErrorHandler} error listener callback. When you call
* `PhutilErrorHandler::setErrorListener()`, you must pass a callback function
* `PhutilErrorHandler::addErrorListener()`, you must pass a callback function
* with the same signature as this one.
*
* NOTE: @{class:PhutilErrorHandler} handles writing messages to the error

View file

@ -1103,12 +1103,23 @@ final class Filesystem extends Phobject {
}
return null;
}
$stdout = head($stdout);
// These are the only file extensions that can be executed directly
// when using proc_open() with 'bypass_shell'.
$executable_extensions = ['exe', 'bat', 'cmd', 'com'];
foreach ($stdout as $line) {
$path = trim($line);
$ext = pathinfo($path, PATHINFO_EXTENSION);
if (in_array($ext, $executable_extensions)) {
return $path;
}
}
return null;
} else {
list($err, $stdout) = exec_manual('which %s', $binary);
return $err === 0 ? trim($stdout) : null;
}
return $err === 0 ? trim($stdout) : null;
}

View file

@ -83,7 +83,7 @@ final class PhutilErrorLog
}
public function onError($event, $value, array $metadata) {
// If we've set "error_log" to a real file, so messages won't be output to
// If we've set "error_log" to a real file, messages won't be output to
// stderr by default. Copy them to stderr.
if ($this->logPath === null) {

View file

@ -229,7 +229,10 @@ final class PhutilOAuth1Future extends FutureProxy {
$consumer_secret = $this->consumerSecret->openEnvelope();
}
$key = urlencode($consumer_secret).'&'.urlencode($this->tokenSecret);
$key = urlencode($consumer_secret).'&';
if ($this->tokenSecret !== null) {
$key .= urlencode($this->tokenSecret);
}
switch ($this->signatureMethod) {
case 'HMAC-SHA1':

View file

@ -37,11 +37,12 @@ final class ArcanistComposerLinter extends ArcanistLinter {
}
private function lintComposerJson($path) {
$composer_hash = md5(Filesystem::readFile(dirname($path).'/composer.json'));
$composer_hash = self::getContentHash(
Filesystem::readFile(dirname($path).'/composer.json'));
$composer_lock = phutil_json_decode(
Filesystem::readFile(dirname($path).'/composer.lock'));
if ($composer_hash !== $composer_lock['hash']) {
if ($composer_hash !== $composer_lock['content-hash']) {
$this->raiseLintAtPath(
self::LINT_OUT_OF_DATE,
pht(
@ -52,4 +53,68 @@ final class ArcanistComposerLinter extends ArcanistLinter {
}
}
/**
* Returns the md5 hash of the sorted content of the composer.json file.
*
* This function copied from
* https://github.com/
* composer/composer/blob/1.5.2/src/Composer/Package/Locker.php
* and has the following license:
*
* Copyright (c) Nils Adermann, Jordi Boggiano
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*
* @param string $composer_file_contents The contents of the composer file.
*
* @return string
*/
public static function getContentHash($composer_file_contents) {
$content = json_decode($composer_file_contents, true);
$relevant_keys = array(
'name',
'version',
'require',
'require-dev',
'conflict',
'replace',
'provide',
'minimum-stability',
'prefer-stable',
'repositories',
'extra',
);
$relevant_content = array();
foreach (array_intersect($relevant_keys, array_keys($content)) as $key) {
$relevant_content[$key] = $content[$key];
}
if (isset($content['config']['platform'])) {
$relevant_content['config']['platform'] = $content['config']['platform'];
}
ksort($relevant_content);
return md5(json_encode($relevant_content));
}
}

View file

@ -133,7 +133,19 @@ abstract class ArcanistExternalLinter extends ArcanistFutureLinter {
* @task bin
*/
final public function getBinary() {
return coalesce($this->bin, $this->getDefaultBinary());
$bin = coalesce($this->bin, $this->getDefaultBinary());
if (phutil_is_windows()) {
// On Windows, we use proc_open with 'bypass_shell' option, which will
// resolve %PATH%, but not %PATHEXT% (unless the extension is .exe).
// Therefore find the right binary ourselves.
// If we can't find it, leave it unresolved, as this string will be
// used in some error messages elsewhere.
$resolved = Filesystem::resolveBinary($bin);
if ($resolved) {
return $resolved;
}
}
return $bin;
}
/**

View file

@ -1,5 +1,6 @@
/* jshint maxerr: 1 */
console.log('foobar')
console.log(
{
~~~~~~~~~~
disabled:2:22:E043
warning:2:22:W033
disabled:3:1:E043
error:3:1:E019

View file

@ -3,5 +3,5 @@
</lang>
</languages>
~~~~~~~~~~
error:3:16:XML76:LibXML Error
error:3:12:XML76:LibXML Error
error:4:1:XML5:LibXML Error

View file

@ -469,10 +469,12 @@ EOPHP;
$this->fileSymbolMap = $symbol_map;
// We're done building the cache, so write it out immediately. Note that
// we've only retained entries for files we found, so this implicitly cleans
// out old cache entries.
$this->writeSymbolCache($symbol_map, $source_map);
if ($futures) {
// We're done building/updating the cache, so write it out immediately.
// Note that we've only retained entries for files we found, so this
// implicitly cleans out old cache entries.
$this->writeSymbolCache($symbol_map, $source_map);
}
// Our map is up to date, so either show it on stdout or write it to disk.
$this->librarySymbolMap = $this->buildLibraryMap($symbol_map);

View file

@ -762,7 +762,7 @@ final class ArcanistBundle extends Phobject {
$old_data = $this->getBlob($old_phid, $name);
}
$old_length = strlen($old_data);
$old_length = phutil_nonempty_string($old_data) ? strlen($old_data) : 0;
// Here, and below, the binary will be emitted with base85 encoding. This
// encoding encodes each 4 bytes of input in 5 bytes of output, so we may
@ -795,7 +795,7 @@ final class ArcanistBundle extends Phobject {
$new_data = $this->getBlob($new_phid, $name);
}
$new_length = strlen($new_data);
$new_length = phutil_nonempty_string($new_data) ? strlen($new_data) : 0;
$this->reserveBytes($new_length * 5 / 4);
if ($new_data === null) {

View file

@ -11,6 +11,14 @@ final class PlatformSymbols
return 'Phorge';
}
public static function getPlatformClientPath() {
return 'arcanist/';
}
public static function getPlatformServerPath() {
return 'phorge/';
}
public static function getProductNames() {
return array(
self::getPlatformClientName(),

View file

@ -41,44 +41,44 @@ final class PhutilCowsay extends Phobject {
$template = $this->template;
// Real ".cow" files are Perl scripts which define a variable called
// "$the_cow". We aren't going to interpret Perl, so strip all this stuff
// (and any comments in the file) away.
$template = phutil_split_lines($template, true);
$keep = array();
$is_perl_cowfile = false;
foreach ($template as $key => $line) {
if (preg_match('/^#/', $line)) {
continue;
}
if (preg_match('/^\s*\\$the_cow/', $line)) {
$is_perl_cowfile = true;
continue;
}
if (preg_match('/^\s*EOC\s*$/', $line)) {
continue;
}
$keep[] = $line;
}
$template = implode('', $keep);
// "$the_cow". We aren't going to interpret Perl, so just get everything
// between the EOC (End Of Cow) tokens. The initial EOC might be in
// quotes, and might have a semicolon.
// We apply regexp modifiers
// * 's' to make . match newlines within the EOC ... EOC block
// * 'm' so we can use ^ to match start of line within the multiline string
$matches = null;
if (
preg_match('/\$the_cow/', $template) &&
preg_match('/EOC[\'"]?;?.*?^(.*?)^EOC/sm', $template, $matches)
) {
$template = $matches[1];
// Original .cow files are perl scripts which contain escaped sequences.
// We attempt to unescape here by replacing any character preceded by a
// backslash/escape with just that character.
if ($is_perl_cowfile) {
// Original .cow files are perl scripts which contain escaped sequences.
// We attempt to unescape here by replacing any character preceded by a
// backslash/escape with just that character.
$template = preg_replace(
'/\\\\(.)/',
'$1',
$template);
} else {
// Text template. Just strip away comments.
$template = preg_replace('/^#.*$/', '', $template);
}
$template = preg_replace_callback(
$token_patterns = array(
'/\\$([a-z]+)/',
array($this, 'replaceTemplateVariable'),
$template);
if ($template === false) {
throw new Exception(
pht(
'Failed to replace template variables while rendering cow!'));
'/\\${([a-z]+)}/',
);
foreach ($token_patterns as $token_pattern) {
$template = preg_replace_callback(
$token_pattern,
array($this, 'replaceTemplateVariable'),
$template);
if ($template === false) {
throw new Exception(
pht('Failed to replace template variables while rendering cow!'));
}
}
$lines = $this->text;

View file

@ -1,5 +1,5 @@
# test case for original perl-script cowfile
$the_cow =
$the_cow = <<EOC
$thoughts
$thoughts
/---\\__/---\\\\
@ -9,6 +9,7 @@ $the_cow =
/ ..--.. \\\\
| \\ .... / ||
\\---/--\\---//
EOC
~~~~~~~~~~
{
"text": "I made a friend!",

View file

@ -0,0 +1,11 @@
__________________
< How are my eyes? >
------------------
\
\
__
UooU\.'@@@@@@`.
\__/(@@@@@@@@@@)
(@@@@@@@@)
`YY~~~~YY'
|| ||

View file

@ -0,0 +1,13 @@
$thoughts
$thoughts
__
U${eyes}U\.'@@@@@@`.
\__/(@@@@@@@@@@)
(@@@@@@@@)
`YY~~~~YY'
|| ||
~~~~~~~~~~
{
"text": "How are my eyes?",
"eyes": "oo"
}

View file

@ -0,0 +1,7 @@
__________________
< How are my eyes? >
------------------
\ ,__,
\ (oo)____
(__) )\
||--|| *

View file

@ -0,0 +1,12 @@
$eyes = ".." unless ($eyes);
$the_cow = <<EOC;
$thoughts ,__,
$thoughts ($eyes)____
(__) )\\
$tongue||--|| *
EOC
~~~~~~~~~~
{
"text": "How are my eyes?",
"eyes": "oo"
}

View file

@ -479,8 +479,8 @@ abstract class ArcanistWorkflow extends Phobject {
// If we have `token`, this server supports the simpler, new-style
// token-based authentication. Use that instead of all the certificate
// stuff.
$token = idx($credentials, 'token', '');
if (strlen($token)) {
$token = idx($credentials, 'token');
if (phutil_nonempty_string($token)) {
$conduit = $this->getConduit();
$conduit->setConduitToken($token);
@ -2244,8 +2244,8 @@ abstract class ArcanistWorkflow extends Phobject {
protected function getModernUnitDictionary(array $map) {
$map = $this->getModernCommonDictionary($map);
$details = idx($map, 'userData', '');
if (strlen($details)) {
$details = idx($map, 'userData');
if (phutil_nonempty_string($details)) {
$map['details'] = (string)$details;
}
unset($map['userData']);