diff --git a/.editorconfig b/.editorconfig index d90dd34b99..a25dbdfc85 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ [*] -insert_final_newline = false +insert_final_newline = true indent_style = tab indent_size = 4 diff --git a/Dockerfile b/Dockerfile index 4d9443971e..7cc479b926 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM tgstation/byond:512.1453 as base +FROM tgstation/byond:512.1463 as base FROM base as build_base @@ -25,7 +25,7 @@ COPY dependencies.sh . RUN /bin/bash -c "source dependencies.sh \ && git fetch --depth 1 origin \$RUST_G_VERSION" \ && git checkout FETCH_HEAD \ - && cargo build --release + && ~/.cargo/bin/cargo build --release FROM build_base as bsql @@ -87,4 +87,4 @@ RUN ln -s /tgstation/libBSQL.so /root/.byond/bin/libBSQL.so VOLUME [ "/tgstation/config", "/tgstation/data" ] -ENTRYPOINT [ "DreamDaemon", "tgstation.dmb", "-port", "1337", "-trusted", "-close", "-verbose" ] \ No newline at end of file +ENTRYPOINT [ "DreamDaemon", "tgstation.dmb", "-port", "1337", "-trusted", "-close", "-verbose" ] diff --git a/dependencies.sh b/dependencies.sh index 644cdd3acc..a75940088f 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash #Project dependencies file #Final authority on what's required to fully build the project @@ -11,7 +11,7 @@ export BYOND_MINOR=${LIST[1]} unset LIST #rust_g git tag -export RUST_G_VERSION=0.4.1 +export RUST_G_VERSION=0.4.2 #bsql git tag export BSQL_VERSION=v1.4.0.0 diff --git a/rust_g.dll b/rust_g.dll index 3d38adccd5..27ee495684 100644 Binary files a/rust_g.dll and b/rust_g.dll differ diff --git a/tools/Redirector/Redirector.dm b/tools/Redirector/Redirector.dm index 2650fdd76a..fb435594e2 100644 --- a/tools/Redirector/Redirector.dm +++ b/tools/Redirector/Redirector.dm @@ -15,10 +15,10 @@ var/admin_weight = 5 // admins are more likely to join a server with less admins var/player_substr = "players=" // search for this substring to locate # of players var/admin_substr = "admins=" // search for this to locate # of admins -world +/world name = "TGstation Redirector" -world/New() +/world/New() ..() gen_configs() @@ -29,7 +29,7 @@ world/New() var/link = "" -mob/Login() +/mob/Login() ..() var/list/weights = list() @@ -60,7 +60,7 @@ mob/Login() src << link(serverlink) -proc/extract(var/data, var/type = PLAYERS) +/proc/extract(var/data, var/type = PLAYERS) var/nextpos = 0 diff --git a/tools/WebhookProcessor/github_webhook_processor.php b/tools/WebhookProcessor/github_webhook_processor.php index e2039869d1..76dd158727 100644 --- a/tools/WebhookProcessor/github_webhook_processor.php +++ b/tools/WebhookProcessor/github_webhook_processor.php @@ -14,6 +14,13 @@ * @author Miloslav Hula (https://github.com/milo) */ +define('S_LINK_EMBED', 1<<0); +define('S_MENTIONS', 1<<1); +define('S_MARKDOWN', 1<<2); +define('S_HTML_COMMENTS', 1<<3); + +define('F_UNVALIDATED_USER', 1<<0); +define('F_SECRET_PR', 1<<1); //CONFIGS ARE IN SECRET.PHP, THESE ARE JUST DEFAULTS! @@ -32,17 +39,21 @@ $validation = "org"; $validation_count = 1; $tracked_branch = 'master'; $require_changelogs = false; +$discordWebHooks = array(); require_once 'secret.php'; //CONFIG END +function log_error($msg) { + echo htmlSpecialChars($msg); + file_put_contents('htwebhookerror.log', '['.date(DATE_ATOM).'] '.$msg.PHP_EOL, FILE_APPEND); +} set_error_handler(function($severity, $message, $file, $line) { throw new \ErrorException($message, 0, $severity, $file, $line); }); set_exception_handler(function($e) { header('HTTP/1.1 500 Internal Server Error'); - echo "Error on line {$e->getLine()}: " . htmlSpecialChars($e->getMessage()); - file_put_contents('htwebhookerror.log', '['.date(DATE_ATOM).'] '."Error on line {$e->getLine()}: " . $e->getMessage().PHP_EOL, FILE_APPEND); + log_error('Error on line ' . $e->getLine() . ': ' . $e->getMessage()); die(); }); $rawPost = NULL; @@ -110,24 +121,38 @@ switch (strtolower($_SERVER['HTTP_X_GITHUB_EVENT'])) { die(); } -function apisend($url, $method = 'GET', $content = NULL) { - global $apiKey; +function apisend($url, $method = 'GET', $content = null, $authorization = null) { if (is_array($content)) $content = json_encode($content); + $headers = array(); + $headers[] = 'Content-type: application/json'; + if ($authorization) + $headers[] = 'Authorization: ' . $authorization; + $scontext = array('http' => array( - 'method' => $method, - 'header' => - "Content-type: application/json\r\n". - 'Authorization: token ' . $apiKey, + 'method' => $method, + 'header' => implode("\r\n", $headers), 'ignore_errors' => true, 'user_agent' => 'tgstation13.org-Github-Automation-Tools' )); + if ($content) $scontext['http']['content'] = $content; return file_get_contents($url, false, stream_context_create($scontext)); + } + +function github_apisend($url, $method = 'GET', $content = NULL) { + global $apiKey; + return apisend($url, $method, $content, 'token ' . $apiKey); +} + +function discord_webhook_send($webhook, $content) { + return apisend($webhook, 'POST', $content); +} + function validate_user($payload) { global $validation, $validation_count; $query = array(); @@ -148,7 +173,7 @@ function validate_user($payload) { $querystring = ''; foreach($query as $key => $value) $querystring .= ($querystring == '' ? '' : '+') . urlencode($key) . ':' . urlencode($value); - $res = apisend('https://api.github.com/search/issues?q='.$querystring); + $res = github_apisend('https://api.github.com/search/issues?q='.$querystring); $res = json_decode($res, TRUE); return $res['total_count'] >= (int)$validation_count; @@ -156,9 +181,9 @@ function validate_user($payload) { function get_labels($payload){ $url = $payload['pull_request']['issue_url'] . '/labels'; - $existing_labels = json_decode(apisend($url), true); + $existing_labels = json_decode(github_apisend($url), true); $existing = array(); - foreach($existing_labels as $label) + foreach((array) $existing_labels as $label) $existing[] = $label['name']; return $existing; } @@ -174,7 +199,6 @@ function check_tag_and_replace($payload, $title_tag, $label, &$array_to_add_labe function set_labels($payload, $labels, $remove) { $existing = get_labels($payload); - $tags = array(); $tags = array_merge($labels, $existing); @@ -188,20 +212,20 @@ function set_labels($payload, $labels, $remove) { $final[] = $t; $url = $payload['pull_request']['issue_url'] . '/labels'; - echo apisend($url, 'PUT', $final); + echo github_apisend($url, 'PUT', $final); } //rip bs-12 function tag_pr($payload, $opened) { //get the mergeable state $url = $payload['pull_request']['url']; - $payload['pull_request'] = json_decode(apisend($url), TRUE); + $payload['pull_request'] = json_decode(github_apisend($url), TRUE); if($payload['pull_request']['mergeable'] == null) { //STILL not ready. Give it a bit, then try one more time sleep(10); - $payload['pull_request'] = json_decode(apisend($url), TRUE); + $payload['pull_request'] = json_decode(github_apisend($url), TRUE); } - + $tags = array(); $title = $payload['pull_request']['title']; if($opened) { //you only have one shot on these ones so as to not annoy maintainers @@ -249,16 +273,16 @@ function remove_ready_for_review($payload, $labels = null){ if($index !== FALSE) unset($labels[$index]); $url = $payload['pull_request']['issue_url'] . '/labels'; - apisend($url, 'PUT', $labels); + github_apisend($url, 'PUT', $labels); } function dismiss_review($payload, $id, $reason){ $content = array('message' => $reason); - apisend($payload['pull_request']['url'] . '/reviews/' . $id . '/dismissals', 'PUT', $content); + github_apisend($payload['pull_request']['url'] . '/reviews/' . $id . '/dismissals', 'PUT', $content); } function get_reviews($payload){ - return json_decode(apisend($payload['pull_request']['url'] . '/reviews'), true); + return json_decode(github_apisend($payload['pull_request']['url'] . '/reviews'), true); } function check_ready_for_review($payload, $labels = null, $remove = array()){ @@ -308,7 +332,7 @@ function check_ready_for_review($payload, $labels = null, $remove = array()){ if(count($reviews_ids_with_changes_requested) > 0){ //now get the review comments for the offending reviews - $review_comments = json_decode(apisend($payload['pull_request']['review_comments_url']), true); + $review_comments = json_decode(github_apisend($payload['pull_request']['review_comments_url']), true); foreach($review_comments as $C){ //make sure they are part of an offending review @@ -350,7 +374,7 @@ function check_dismiss_changelog_review($payload){ if($R['body'] == $review_message && strtolower($R['state']) == 'changes_requested') return; //otherwise make it ourself - apisend($payload['pull_request']['url'] . '/reviews', 'POST', array('body' => $review_message, 'event' => 'REQUEST_CHANGES')); + github_apisend($payload['pull_request']['url'] . '/reviews', 'POST', array('body' => $review_message, 'event' => 'REQUEST_CHANGES')); } else //kill previous reviews @@ -403,28 +427,191 @@ function handle_pr($payload) { return; } + $pr_flags = 0; if (strpos(strtolower($payload['pull_request']['title']), '[s]') !== false) { - echo "PR Announcement Halted; Secret tag detected.\n"; - return; + $pr_flags |= F_SECRET_PR; } if (!$validated) { - echo "PR Announcement Halted; User not validated.\n"; - return; + $pr_flags |= F_UNVALIDATED_USER; } + discord_announce($action, $payload, $pr_flags); + game_announce($action, $payload, $pr_flags); + +} + +function filter_announce_targets($targets, $owner, $repo, $action, $pr_flags) { + foreach ($targets as $i=>$target) { + if (isset($target['exclude_events']) && in_array($action, array_map('strtolower', $target['exclude_events']))) { + unset($targets[$i]); + continue; + } + if (isset($target['announce_secret']) && $target['announce_secret']) { + if (!($pr_flags & F_SECRET_PR) && $target['announce_secret'] === 'only') { + unset($targets[$i]); + continue; + } + } else if ($pr_flags & F_SECRET_PR) { + unset($targets[$i]); + continue; + } + + if (isset($target['announce_unvalidated']) && $target['announce_unvalidated']) { + if (!($pr_flags & F_UNVALIDATED_USER) && $target['announce_unvalidated'] === 'only') { + unset($targets[$i]); + continue; + } + } else if ($pr_flags & F_UNVALIDATED_USER) { + unset($targets[$i]); + continue; + } + + $wildcard = false; + if (isset($target['include_repos'])) { + foreach ($target['include_repos'] as $match_string) { + $owner_repo_pair = explode('/', strtolower($match_string)); + if (count($owner_repo_pair) != 2) { + log_error('Bad include repo: `'. $match_string.'`'); + continue; + } + if (strtolower($owner) == $owner_repo_pair[0]) { + if (strtolower($repo) == $owner_repo_pair[1]) + continue 2; //don't parse excludes when we have an exact include match + if ($owner_repo_pair[1] == '*') { + $wildcard = true; + continue; //do parse excludes when we have a wildcard match (but check the other entries for exact matches first) + } + } + } + if (!$wildcard) { + unset($targets[$i]); + continue; + } + } + + if (isset($target['exclude_repos'])) + foreach ($target['exclude_repos'] as $match_string) { + $owner_repo_pair = explode('/', strtolower($match_string)); + if (count($owner_repo_pair) != 2) { + log_error('Bad exclude repo: `'. $match_string.'`'); + continue; + } + if (strtolower($owner) == $owner_repo_pair[0]) { + if (strtolower($repo) == $owner_repo_pair[1]) { + unset($targets[$i]); + continue 2; + } + if ($owner_repo_pair[1] == '*') { + if ($wildcard) + log_error('Identical wildcard include and exclude: `'.$match_string.'`. Excluding.'); + unset($targets[$i]); + continue 2; + } + } + } + } + return $targets; +} + +function game_announce($action, $payload, $pr_flags) { + global $servers; + $msg = '['.$payload['pull_request']['base']['repo']['full_name'].'] Pull Request '.$action.' by '.htmlSpecialChars($payload['sender']['login']).': '.htmlSpecialChars('#'.$payload['pull_request']['number'].' '.$payload['pull_request']['user']['login'].' - '.$payload['pull_request']['title']).''; - sendtoallservers('?announce='.urlencode($msg), $payload); + + $game_servers = filter_announce_targets($servers, $payload['pull_request']['base']['repo']['owner']['login'], $payload['pull_request']['base']['repo']['name'], $action, $pr_flags); + + $msg = '?announce='.urlencode($msg).'&payload='.urlencode(json_encode($payload)); + + foreach ($game_servers as $serverid => $server) { + $server_message = $msg; + if (isset($server['comskey'])) + $server_message .= '&key='.urlencode($server['comskey']); + game_server_send($server['address'], $server['port'], $server_message); + } + +} + +function discord_announce($action, $payload, $pr_flags) { + global $discordWebHooks; + $color; + switch ($action) { + case 'reopened': + case 'opened': + $color = 0x2cbe4e; + break; + case 'closed': + $color = 0xcb2431; + break; + case 'merged': + $color = 0x6f42c1; + break; + default: + return; + } + $data = array( + 'username' => 'GitHub', + 'avatar_url' => $payload['pull_request']['base']['user']['avatar_url'], + ); + + $content = 'Pull Request #'.$payload['pull_request']['number'].' *'.$action.'* by '.discord_sanitize($payload['sender']['login'])."\n".discord_sanitize($payload['pull_request']['user']['login']).' - __**'.discord_sanitize($payload['pull_request']['title']).'**__'."\n".'<'.$payload['pull_request']['html_url'].'>'; + + $embeds = array( + array( + 'title' => '__**'.discord_sanitize($payload['pull_request']['title'], S_MARKDOWN).'**__', + 'description' => discord_sanitize(str_replace(array("\r\n", "\n"), array(' ', ' '), substr($payload['pull_request']['body'], 0, 320)), S_HTML_COMMENTS), + 'url' => $payload['pull_request']['html_url'], + 'color' => $color, + 'author' => array( + 'name' => discord_sanitize($payload['pull_request']['user']['login'], S_MARKDOWN), + 'url' => $payload['pull_request']['user']['html_url'], + 'icon_url' => $payload['pull_request']['user']['avatar_url'] + ), + 'footer' => array( + 'text' => '#'.$payload['pull_request']['number'].' '.discord_sanitize($payload['pull_request']['base']['repo']['full_name'], S_MARKDOWN).' '.discord_sanitize($payload['pull_request']['head']['ref'], S_MARKDOWN).' -> '.discord_sanitize($payload['pull_request']['base']['ref'], S_MARKDOWN), + 'icon_url' => $payload['pull_request']['base']['user']['avatar_url'] + ) + ) + ); + $discordWebHook_targets = filter_announce_targets($discordWebHooks, $payload['pull_request']['base']['repo']['owner']['login'], $payload['pull_request']['base']['repo']['name'], $action, $pr_flags); + foreach ($discordWebHook_targets as $discordWebHook) { + $sending_data = $data; + if (isset($discordWebHook['embed']) && $discordWebHook['embed']) { + $sending_data['embeds'] = $embeds; + if (!isset($discordWebHook['no_text']) || !$discordWebHook['no_text']) + $sending_data['content'] = $content; + } else { + $sending_data['content'] = $content; + } + discord_webhook_send($discordWebHook['url'], $sending_data); + } + +} + +function discord_sanitize($text, $flags = S_MENTIONS|S_LINK_EMBED|S_MARKDOWN) { + if ($flags & S_MARKDOWN) + $text = str_ireplace(array('\\', '*', '_', '~', '`', '|'), (array('\\\\', '\\*', '\\_', '\\~', '\\`', '\\|')), $text); + + if ($flags & S_HTML_COMMENTS) + $text = preg_replace('//Uis', '', $text); + + if ($flags & S_MENTIONS) + $text = str_ireplace(array('@everyone', '@here', '<@'), array('`@everyone`', '`@here`', '@<'), $text); + + if ($flags & S_LINK_EMBED) + $text = preg_replace("/((https?|ftp|byond)\:\/\/)([a-z0-9-.]*)\.([a-z]{2,3})(\:[0-9]{2,5})?(\/(?:[a-z0-9+\$_-]\.?)+)*\/?(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?(#[a-z_.-][a-z0-9+\$_.-]*)?/mi", '<$0>', $text); + + return $text; } //creates a comment on the payload issue function create_comment($payload, $comment){ - apisend($payload['pull_request']['comments_url'], 'POST', json_encode(array('body' => $comment))); + github_apisend($payload['pull_request']['comments_url'], 'POST', json_encode(array('body' => $comment))); } //returns the payload issue's labels as a flat array function get_pr_labels_array($payload){ $url = $payload['pull_request']['issue_url'] . '/labels'; - $issue = json_decode(apisend($url), true); + $issue = json_decode(github_apisend($url), true); $result = array(); foreach($issue as $l) $result[] = $l['name']; @@ -492,13 +679,13 @@ function is_maintainer($payload, $author){ $repo_is_org = $payload['pull_request']['base']['repo']['owner']['type'] == 'Organization'; if($maintainer_team_id == null || !$repo_is_org) { $collaburl = str_replace('{/collaborator}', '/' . $author, $payload['pull_request']['base']['repo']['collaborators_url']) . '/permission'; - $perms = json_decode(apisend($collaburl), true); + $perms = json_decode(github_apisend($collaburl), true); $permlevel = $perms['permission']; return $permlevel == 'admin' || $permlevel == 'write'; } else { $check_url = 'https://api.github.com/teams/' . $maintainer_team_id . '/memberships/' . $author; - $result = json_decode(apisend($check_url), true); + $result = json_decode(github_apisend($check_url), true); return isset($result['state']) && $result['state'] == 'active'; } } @@ -530,7 +717,7 @@ $github_diff = null; function get_diff($payload) { global $github_diff; - if ($github_diff === null) { + if ($github_diff === null && $payload['pull_request']['diff_url']) { //go to the diff url $url = $payload['pull_request']['diff_url']; $github_diff = file_get_contents($url); @@ -597,7 +784,7 @@ function checkchangelog($payload, $compile = true) { $foundcltag = false; foreach ($body as $line) { $line = trim($line); - if (substr($line,0,4) == ':cl:' || substr($line,0,1) == '🆑') { + if (substr($line,0,4) == ':cl:' || substr($line,0,1) == '??') { $incltag = true; $foundcltag = true; $pos = strpos($line, " "); @@ -607,7 +794,7 @@ function checkchangelog($payload, $compile = true) { $username = $tmp; } continue; - } else if (substr($line,0,5) == '/:cl:' || substr($line,0,6) == '/ :cl:' || substr($line,0,5) == ':/cl:' || substr($line,0,5) == '/🆑' || substr($line,0,6) == '/ 🆑' ) { + } else if (substr($line,0,5) == '/:cl:' || substr($line,0,6) == '/ :cl:' || substr($line,0,5) == ':/cl:' || substr($line,0,5) == '/??' || substr($line,0,6) == '/ ??' ) { $incltag = false; $changelogbody = array_merge($changelogbody, $currentchangelogblock); continue; @@ -773,25 +960,10 @@ function checkchangelog($payload, $compile = true) { ); $filename = '/html/changelogs/AutoChangeLog-pr-'.$payload['pull_request']['number'].'.yml'; - echo apisend($payload['pull_request']['base']['repo']['url'].'/contents'.$filename, 'PUT', $content); + echo github_apisend($payload['pull_request']['base']['repo']['url'].'/contents'.$filename, 'PUT', $content); } -function sendtoallservers($str, $payload = null) { - global $servers; - if (!empty($payload)) - $str .= '&payload='.urlencode(json_encode($payload)); - foreach ($servers as $serverid => $server) { - $msg = $str; - if (isset($server['comskey'])) - $msg .= '&key='.urlencode($server['comskey']); - $rtn = export($server['address'], $server['port'], $msg); - echo "Server Number $serverid replied: $rtn\n"; - } -} - - - -function export($addr, $port, $str) { +function game_server_send($addr, $port, $str) { // All queries must begin with a question mark (ie "?players") if($str{0} != '?') $str = ('?' . $str); diff --git a/tools/WebhookProcessor/secret.php b/tools/WebhookProcessor/secret.php index 7d763c3d6c..9b8a27d490 100644 --- a/tools/WebhookProcessor/secret.php +++ b/tools/WebhookProcessor/secret.php @@ -4,7 +4,6 @@ //Github lets you have it sign the message with a secret that you can validate. This prevents people from faking events. //This var should match the secret you configured for this webhook on github. //This is required as otherwise somebody could trick the script into leaking the api key. - $hookSecret = '08ajh0qj93209qj90jfq932j32r'; //Api key for pushing changelogs. @@ -14,23 +13,12 @@ $apiKey = '209ab8d879c0f987d06a09b9d879c0f987d06a09b9d8787d0a089c'; //Used to prevent potential RCEs $repoOwnerAndName = "tgstation/tgstation"; -//servers to announce PRs to -$servers = array(); -/* -$servers[0] = array(); -$servers[0]['address'] = 'game.tgstation13.org'; -$servers[0]['port'] = '1337'; -$servers[0]['comskey'] = '89aj90cq2fm0amc90832mn9rm90'; -$servers[1] = array(); -$servers[1]['address'] = 'game.tgstation13.org'; -$servers[1]['port'] = '2337'; -$servers[1]['comskey'] = '89aj90cq2fm0amc90832mn9rm90'; -*/ - +//Auto update settings $enable_live_tracking = true; //auto update this file from the repository $path_to_script = 'tools/WebhookProcessor/github_webhook_processor.php'; $tracked_branch = "master"; +//PR balance settings. $trackPRBalance = true; //set this to false to disable PR balance tracking $prBalanceJson = ''; //Set this to the path you'd like the writable pr balance file to be stored, not setting it writes it to the working directory $startingPRBalance = 5; //Starting balance for never before seen users @@ -44,6 +32,7 @@ $maintainer_team_id = 133041; //org team id that is exempt from PR balance syste // "org" - user has to have a pr merged in any repo in the organization (for repos owned directly by users, this applies to any repo directly owned by the same user.) // "disable" - disables. //defaults to org if left blank or given invalid values. +//This can also be ignored on a per webhook or per game server bases. $validation = "org"; //how many merged prs must they have under the rules above to have their pr announced to the game servers. @@ -51,3 +40,94 @@ $validation_count = 1; //enforce changelogs on PRs $require_changelogs = false; + +/* + * Announcement Settings. + * Allows you to announce prs to discord webhooks or the game servers + */ + +/* Common configs: +The following config items can be added to both game server and discord announcement endpoints. Simply replace the $servers part with $discordWebHooks: + +include_repos - List of repos in owner/repo format to send to this endpoint. (defaults to all repos if not defined) + * can be given in place of repo to match all repos under an organization +$servers[$configitem]['include_repos'][] = "tgstation/*"; + +exclude_repos - List of repos in owner/repo format to not send to this endpoint. + * can be given in place of repo to match all repos under an organization +$servers[$configitem]['exclude_repos'][] = 'tgstation/TerraGov-Marine-Corps'; +$servers[$configitem]['exclude_repos'][] = 'tgstation/tgstation13.org'; + +exclude_events - List of events to not announce, values: opened, closed, reopened, or merged +$servers[$configitem]['exclude_events'][] = 'closed'; +$servers[$configitem]['exclude_events'][] = 'reopened'; + +announce_secret - Announce secret/security prs that have a [s] in front of the title? Defaults to no. + Can also be set to 'only' to only announce secret prs. +$servers[$configitem]['announce_secret'] = false; +$servers[$configitem]['announce_secret'] = 'only'; + +announce_unvalidated - Announce prs by unvalidated users (see the validation setting above)? Defaults to no. + Can also be set to 'only' to only announce prs by unvalidated users. +$servers[$configitem]['announce_unvalidated'] = false; + +//Note: the same webhook or game server can be given in mutiple announce endpoints with different settings, allowing you to say, have embeds only show on prs to certain repos by excluding the repo in a endpoint with embed = false, and including the repo in a endpoint with embed = true true. This could also be used to only block closed and reopened events on prs by unvalidated users. + + + +//Game servers to announce PRs to. +/* +$configitem = -1;//ignore me + +//Game Server Start +$servers[++$configitem] = array(); +$servers[$configitem]['address'] = 'game.tgstation13.org'; +$servers[$configitem]['port'] = '1337'; +$servers[$configitem]['comskey'] = '89aj90cq2fm0amc90832mn9rm90'; +//Game Server End + +//Game Server Start +$servers[++$configitem] = array(); +$servers[$configitem]['address'] = 'game.tgstation13.org'; +$servers[$configitem]['port'] = '2337'; +$servers[$configitem]['comskey'] = '89aj90cq2fm0amc90832mn9rm90'; +//Game Server End + +unset($configitem);//ignore +*/ + +//discord webhooks to announce PRs to. +/* +$configitem = -1;//ignore me + +//Discord Webhook Start +$discordWebHooks[++$configitem] = array(); + +// Webhook Url (you can get this from discord via the webhook setting menu of the server or a channel.) +$discordWebHooks[$configitem]['url'] = 'https://discordapp.com/api/webhooks/538933489920245771/xaoYtVuype-P1rb_uthQLkh_C4iVL3sjtIvFEp7rsfhbBs8tDsSJgE0a9MNWJaoSPBPK'; + +// show an embed with more info? +$discordWebHooks[$configitem]['embed'] = true; + +// if the above is true, don't include the text portion before the embed. +// (This option is not advised as it's not compatible with users who disable embeds). +$discordWebHooks[$configitem]['no_text'] = false; +//Discord Webhook End + +//Discord Webhook Start +$discordWebHooks[++$configitem] = array(); + +// Webhook Url (you can get this from discord via the webhook setting menu of the server or a channel.) +$discordWebHooks[$configitem]['url'] = 'https://discordapp.com/api/webhooks/538933686956064769/q0uDel7S6eutvRIyEwsuZo_ppzAoxqUNeU2PRChYVsYoJmmn2f2YYSDoMjy9FhhXKqpI'; + +// show an embed with more info? +$discordWebHooks[$configitem]['embed'] = true; + +// if the above is true, don't include the text portion before the embed. +// (This option is not advised as it's not compatible with users who disable embeds). +$discordWebHooks[$configitem]['no_text'] = false; +//Discord Webhook End +*/ + +unset($configitem); //ignore + diff --git a/tools/mapmerge2/map_scripts/apc_pixel_offsets.txt b/tools/mapmerge2/map_scripts/apc_pixel_offsets.txt new file mode 100644 index 0000000000..30b5ba2d5d --- /dev/null +++ b/tools/mapmerge2/map_scripts/apc_pixel_offsets.txt @@ -0,0 +1,4 @@ +/obj/machinery/power/apc/@SUBTYPES{dir = 1} : @OLD{@OLD; pixel_y = 23} +/obj/machinery/power/apc/@SUBTYPES{dir = 2} : @OLD{@OLD; pixel_y = -23} +/obj/machinery/power/apc/@SUBTYPES{dir = 4} : @OLD{@OLD; pixel_x = 24} +/obj/machinery/power/apc/@SUBTYPES{dir = 8} : @OLD{@OLD; pixel_x = -25} diff --git a/tools/mapmerge2/map_scripts/cornersfix.txt b/tools/mapmerge2/map_scripts/cornersfix.txt new file mode 100644 index 0000000000..d9ff8a2023 --- /dev/null +++ b/tools/mapmerge2/map_scripts/cornersfix.txt @@ -0,0 +1,30 @@ +/obj/effect/turf_decal/stripes/corner {dir=1} : /obj/item/bear_armor {@OLD;dir=8} +/obj/effect/turf_decal/stripes/corner {dir=2} : /obj/effect/turf_decal/stripes/corner {@OLD;dir=@SKIP} +/obj/effect/turf_decal/stripes/corner {dir=4} : /obj/effect/turf_decal/stripes/corner {@OLD;dir=1} +/obj/effect/turf_decal/stripes/corner {dir=8} : /obj/effect/turf_decal/stripes/corner {@OLD;dir=4} +/obj/item/bear_armor {dir=8} : /obj/effect/turf_decal/stripes/corner {@OLD} +/obj/effect/turf_decal/stripes/red/corner {dir=1} : /obj/item/bear_armor {@OLD;dir=8} +/obj/effect/turf_decal/stripes/red/corner {dir=2} : /obj/effect/turf_decal/stripes/red/corner {@OLD;dir=@SKIP} +/obj/effect/turf_decal/stripes/red/corner {dir=4} : /obj/effect/turf_decal/stripes/red/corner {@OLD;dir=1} +/obj/effect/turf_decal/stripes/red/corner {dir=8} : /obj/effect/turf_decal/stripes/red/corner {@OLD;dir=4} +/obj/item/bear_armor {dir=8} : /obj/effect/turf_decal/stripes/red/corner {@OLD} +/obj/effect/turf_decal/stripes/white/corner {dir=1} : /obj/item/bear_armor {@OLD;dir=8} +/obj/effect/turf_decal/stripes/white/corner {dir=2} : /obj/effect/turf_decal/stripes/white/corner {@OLD;dir=@SKIP} +/obj/effect/turf_decal/stripes/white/corner {dir=4} : /obj/effect/turf_decal/stripes/white/corner {@OLD;dir=1} +/obj/effect/turf_decal/stripes/white/corner {dir=8} : /obj/effect/turf_decal/stripes/white/corner {@OLD;dir=4} +/obj/item/bear_armor {dir=8} : /obj/effect/turf_decal/stripes/white/corner {@OLD} +/obj/effect/turf_decal/box/corners {dir=1} : /obj/item/bear_armor {@OLD;dir=8} +/obj/effect/turf_decal/box/corners {dir=2} : /obj/effect/turf_decal/box/corners {@OLD;dir=@SKIP} +/obj/effect/turf_decal/box/corners {dir=4} : /obj/effect/turf_decal/box/corners {@OLD;dir=1} +/obj/effect/turf_decal/box/corners {dir=8} : /obj/effect/turf_decal/box/corners {@OLD;dir=4} +/obj/item/bear_armor {dir=8} : /obj/effect/turf_decal/box/corners {@OLD} +/obj/effect/turf_decal/box/red/corners {dir=1} : /obj/item/bear_armor {@OLD;dir=8} +/obj/effect/turf_decal/box/red/corners {dir=2} : /obj/effect/turf_decal/box/red/corners {@OLD;dir=@SKIP} +/obj/effect/turf_decal/box/red/corners {dir=4} : /obj/effect/turf_decal/box/red/corners {@OLD;dir=1} +/obj/effect/turf_decal/box/red/corners {dir=8} : /obj/effect/turf_decal/box/red/corners {@OLD;dir=4} +/obj/item/bear_armor {dir=8} : /obj/effect/turf_decal/box/red/corners {@OLD} +/obj/effect/turf_decal/box/white/corners {dir=1} : /obj/item/bear_armor {@OLD;dir=8} +/obj/effect/turf_decal/box/white/corners {dir=2} : /obj/effect/turf_decal/box/white/corners {@OLD;dir=@SKIP} +/obj/effect/turf_decal/box/white/corners {dir=4} : /obj/effect/turf_decal/box/white/corners {@OLD;dir=1} +/obj/effect/turf_decal/box/white/corners {dir=8} : /obj/effect/turf_decal/box/white/corners {@OLD;dir=4} +/obj/item/bear_armor {dir=8} : /obj/effect/turf_decal/box/white/corners {@OLD} \ No newline at end of file diff --git a/tools/mapmerge2/requirements.txt b/tools/mapmerge2/requirements.txt index 095843b2d2..adb42438e7 100644 --- a/tools/mapmerge2/requirements.txt +++ b/tools/mapmerge2/requirements.txt @@ -1,3 +1,3 @@ -pygit2==0.26.0 +pygit2==0.27.2 bidict==0.13.1 Pillow==5.1.0 diff --git a/tools/mapmerge2/update_paths.py b/tools/mapmerge2/update_paths.py index 6359b6bb30..2c316e941d 100644 --- a/tools/mapmerge2/update_paths.py +++ b/tools/mapmerge2/update_paths.py @@ -23,14 +23,14 @@ Old paths properties: """ default_map_directory = "../../_maps" -replacement_re = re.compile('\s*([^{]*)\s*(\{(.*)\})?') +replacement_re = re.compile(r'\s*(?P[^{]*)\s*(\{(?P.*)\})?') #urgent todo: replace with actual parser, this is slow as janitor in crit -split_re = re.compile('((?:[A-Za-z0-9_\-$]+)\s*=\s*(?:"(?:.+?)"|[^";]*)|@OLD)') +split_re = re.compile(r'((?:[A-Za-z0-9_\-$]+)\s*=\s*(?:"(?:.+?)"|[^";]*)|@OLD)') def props_to_string(props): - return "{{{0}}}".format(";".join([k+" = "+props[k] for k in props])) + return "{{{}}}".format(";".join([f"{k} = {v}" for k, v in props.items()])) def string_to_props(propstring, verbose = False): @@ -48,8 +48,8 @@ def string_to_props(propstring, verbose = False): def parse_rep_string(replacement_string, verbose = False): # translates /blah/blah {meme = "test",} into path,prop dictionary tuple match = re.match(replacement_re, replacement_string) - path = match.group(1) - props = match.group(3) + path = match['path'] + props = match['props'] if props: prop_dict = string_to_props(props, verbose) else: @@ -65,9 +65,18 @@ def update_path(dmm_data, replacement_string, verbose=False): new_path, new_path_props = parse_rep_string(replacement_def, verbose) new_paths.append((new_path, new_path_props)) + subtypes = "" + if old_path.endswith("/@SUBTYPES"): + old_path = old_path[:-len("/@SUBTYPES")] + if verbose: + print("Looking for subtypes of", old_path) + subtypes = r"(?:/\w+)*" + + replacement_pattern = re.compile(rf"(?P{re.escape(old_path)}{subtypes})\s*(:?{{(?P.*)}})?$") + def replace_def(match): - if match.group(2): - old_props = string_to_props(match.group(2), verbose) + if match['props']: + old_props = string_to_props(match['props'], verbose) else: old_props = dict() for filter_prop in old_path_props: @@ -83,7 +92,10 @@ def update_path(dmm_data, replacement_string, verbose=False): print("Found match : {0}".format(match.group(0))) out_paths = [] for new_path, new_props in new_paths: - out = new_path + if new_path == "@OLD": + out = match.group('path') + else: + out = new_path out_props = dict() for prop_name, prop_value in new_props.items(): if prop_name == "@OLD": @@ -106,10 +118,9 @@ def update_path(dmm_data, replacement_string, verbose=False): return out_paths def get_result(element): - p = re.compile("{0}\s*({{(.*)}})?$".format(re.escape(old_path))) - match = p.match(element) + match = replacement_pattern.match(element) if match: - return replace_def(match) # = re.sub(p,replace_def,element) + return replace_def(match) else: return [element] @@ -141,10 +152,12 @@ def update_all_maps(map_directory, updates, verbose=False): def main(args): if args.inline: + print("Using replacement:", args.update_source) updates = [args.update_source] else: with open(args.update_source) as f: updates = [line for line in f if line and not line.startswith("#") and not line.isspace()] + print(f"Using {len(updates)} replacements from file:", args.update_source) if args.map: update_map(args.map, updates, verbose=args.verbose) diff --git a/tools/tgs4_scripts/PostCompile.sh b/tools/tgs4_scripts/PostCompile.sh index 36cb797d94..b9f06021ec 100644 --- a/tools/tgs4_scripts/PostCompile.sh +++ b/tools/tgs4_scripts/PostCompile.sh @@ -1,9 +1,13 @@ -#!/bin/sh +#!/bin/bash set -e #load dep exports -. "$1/dependencies.sh" +#need to switch to game dir for Dockerfile weirdness +original_dir=$PWD +cd $1 +. dependencies.sh +cd $original_dir #find out what we have (+e is important for this) set +e @@ -64,7 +68,6 @@ fi echo "Deploying rust-g..." cd rust-g -git clean -fxd git checkout $RUST_G_VERSION ~/.cargo/bin/cargo build --release mv target/release/librust_g.so $1/rust_g @@ -72,10 +75,9 @@ cd .. echo "Deploying BSQL..." cd BSQL -git clean -fxd git checkout $BSQL_VERSION -mkdir mysql -mkdir artifacts +mkdir -p mysql +mkdir -p artifacts cd artifacts cmake .. -DCMAKE_CXX_COMPILER=g++-6 -DMARIA_LIBRARY=/usr/lib/i386-linux-gnu/libmariadb.so.2 make @@ -92,6 +94,7 @@ shopt -s extglob dotglob mv !(build) build shopt -u dotglob +chmod +x build/tools/deploy.sh build/tools/deploy.sh $1 $1/build -rm -rf build \ No newline at end of file +rm -rf build diff --git a/tools/tgs4_scripts/PreSynchronize.bat b/tools/tgs4_scripts/PreSynchronize.bat index 5580dc518f..d2016e21ae 100644 --- a/tools/tgs4_scripts/PreSynchronize.bat +++ b/tools/tgs4_scripts/PreSynchronize.bat @@ -1,836 +1,3 @@ +@echo off - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tgstation/PreSynchronize.bat at cd924de80da882d14fb15f3f0f21102b8c8e7c45 · tgstation/tgstation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content -
- - - - - - - - - - - - -
- -
- -
- - -
- - - -
-
-
- - - - - - - - - - -
-
- -
    -
  • -
    - -
    - - - -
    -
    -
    - - Notifications -
    - - - -
    -
    -
    -
    -
  • - -
  • - -
    -
    - - - -
    -
    - - - -
    - -
  • - -
  • -
    - -
    - -
  • -
- -

- - /tgstation - -

- -
- - - - -
- -
-
- - - Permalink - - - - - -
- -
- - -
- -
-
- - Switch branches/tags -
- -
-
- -
-
- -
-
- - - -
-
- - -
- -
Nothing to show
-
- -
-
-
- -
- - Find file - - - Copy path - -
- -
- - - -
- Fetching contributors… -
- -
- - Cannot retrieve contributors at this time -
-
- - -
-
-
- -
- Raw - Blame - History -
- - - - - -
- -
- 4 lines (2 sloc) - - 99 Bytes -
-
- - - -
- - - - - - - - - - - - - -
@echo off
-
powershell -NoProfile -ExecutionPolicy Bypass -File PreSynchronize.ps1 -game_path %1
- - - -
- -
- -
- - -
- - -
-
- - -
- -
- -
-
- -
- - - - - - -
- - - You can’t perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - - - -
- Press h to open a hovercard with more details. -
- - - - - +powershell -NoProfile -ExecutionPolicy Bypass -File PreSynchronize.ps1 -game_path %1 diff --git a/tools/tgs4_scripts/PreSynchronize.sh b/tools/tgs4_scripts/PreSynchronize.sh index f4e90b85c0..3044b2ba2a 100644 --- a/tools/tgs4_scripts/PreSynchronize.sh +++ b/tools/tgs4_scripts/PreSynchronize.sh @@ -1,981 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tgstation/PreSynchronize.sh at cd924de80da882d14fb15f3f0f21102b8c8e7c45 · tgstation/tgstation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Skip to content -
- - - - - - - - - - - - -
- -
- -
- - -
- - - -
-
-
- - - - - - - - - - -
-
- -
    -
  • -
    - -
    - - - -
    -
    -
    - - Notifications -
    - - - -
    -
    -
    -
    -
  • - -
  • - -
    -
    - - - -
    -
    - - - -
    - -
  • - -
  • -
    - -
    - -
  • -
- -

- - /tgstation - -

- -
- - - - -
- -
-
- - - Permalink - - - - - -
- -
- - -
- -
-
- - Switch branches/tags -
- -
-
- -
-
- -
-
- - - -
-
- - -
- -
Nothing to show
-
- -
-
-
- -
- - Find file - - - Copy path - -
- -
- - - -
- Fetching contributors… -
- -
- - Cannot retrieve contributors at this time -
-
- - -
-
-
- -
- Raw - Blame - History -
- - - - - -
- -
- executable file - - 38 lines (29 sloc) - - 896 Bytes -
-
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#!/bin/sh
-
has_python="$(command -v python3)"
has_git="$(command -v git)"
has_sudo="$(command -v sudo)"
has_pip="$(command -v pip3)"
-
set -e
-
if ! { [ -x "$has_python" ] && [ -x "$has_pip" ] && [ -x "$has_git" ]; }; then
echo "Installing apt dependencies..."
if ! [ -x "$has_sudo" ]; then
apt update
apt install -y python3 python3-pip git
rm -rf /var/lib/apt/lists/*
else
sudo apt update
sudo apt install -y python3 python3-pip git
sudo rm -rf /var/lib/apt/lists/*
fi
fi
-
echo "Installing pip dependencies..."
pip3 install PyYaml beautifulsoup4
-
cd $1
-
echo "Running changelog script..."
python3 tools/ss13_genchangelog.py html/changelog.html html/changelogs
-
echo "Committing changes..."
git add html
-
#we now don't care about failures
set +e
git commit -m "Automatic changelog compile, [ci skip]"
exit 0
- - - -
- -
- -
- - -
- - -
-
- - -
- -
- -
-
- -
- - - - - - -
- - - You can’t perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - - - -
- Press h to open a hovercard with more details. -
- - - - - +#!/bin/sh + +has_python="$(command -v python3)" +has_git="$(command -v git)" +has_sudo="$(command -v sudo)" +has_pip="$(command -v pip3)" + +set -e + +if ! { [ -x "$has_python" ] && [ -x "$has_pip" ] && [ -x "$has_git" ]; }; then + echo "Installing apt dependencies..." + if ! [ -x "$has_sudo" ]; then + apt update + apt install -y python3 python3-pip git + rm -rf /var/lib/apt/lists/* + else + sudo apt update + sudo apt install -y python3 python3-pip git + sudo rm -rf /var/lib/apt/lists/* + fi +fi + +echo "Installing pip dependencies..." +pip3 install PyYaml beautifulsoup4 + +cd $1 + +echo "Running changelog script..." +python3 tools/ss13_genchangelog.py html/changelog.html html/changelogs + +echo "Committing changes..." +git add html + +#we now don't care about failures +set +e +git commit -m "Automatic changelog compile, [ci skip]" +exit 0 diff --git a/tools/travis/build_byond.sh b/tools/travis/build_byond.sh index ec6f53b89c..3a7e00df29 100755 --- a/tools/travis/build_byond.sh +++ b/tools/travis/build_byond.sh @@ -5,19 +5,23 @@ set -e shopt -s globstar if [ "$BUILD_TOOLS" = false ]; then - if grep '"aaa" = \(.+\)' _maps/**/*.dmm; then + if grep -E '^\".+\" = \(.+\)' _maps/**/*.dmm; then echo "Non-TGM formatted map detected. Please convert it using Map Merger!" exit 1 fi; + if grep -P '^\ttag = \"icon' _maps/**/*.dmm; then + echo "tag vars from icon state generation detected in maps, please remove them." + exit 1 + fi; if grep 'step_[xy]' _maps/**/*.dmm; then - echo "step_[xy] variables detected in maps, please remove them." + echo "step_x/step_y variables detected in maps, please remove them." exit 1 fi; if grep 'pixel_[xy] = 0' _maps/**/*.dmm; then - echo "pixel_[xy] = 0 detected in maps, please review to ensure they are not dirty varedits." + echo "pixel_x/pixel_y = 0 variables detected in maps, please review to ensure they are not dirty varedits." fi; - if grep '\td[1-2] =' _maps/**/*.dmm; then - echo "d[1-2] cable variables detected in maps, please remove them." + if grep -P '\td[1-2] =' _maps/**/*.dmm; then + echo "d1/d2 cable variables detected in maps, please remove them." exit 1 fi; if grep '^/area/.+[\{]' _maps/**/*.dmm; then @@ -47,7 +51,7 @@ if [ "$BUILD_TOOLS" = false ]; then else tools/travis/dm.sh -DTRAVISBUILDING tgstation.dme - sh tools/deploy.sh travis_test + tools/deploy.sh travis_test mkdir travis_test/config #test config