2)) exit_with_error('InvalidRequest'); $db = new Database; if (!$db->connect()) exit_with_error('DatabaseConnectionFailure'); $report = $post_data ? json_decode($post_data, true) : array(); $updates = array_get($report, 'buildRequestUpdates'); if ($updates) { verify_worker($db, $report); update_builds($db, $updates); } $requests_fetcher = new BuildRequestsFetcher($db); if ($id) $requests_fetcher->fetch_request($id); else { $triggerable_query = array('name' => array_get($path, 0)); $triggerable = $db->select_first_row('build_triggerables', 'triggerable', $triggerable_query); if (!$triggerable) exit_with_error('TriggerableNotFound', $triggerable_query); $requests_fetcher->fetch_incomplete_requests_for_triggerable($triggerable['triggerable_id']); } $resolve_id = array_get($_GET, 'useLegacyIdResolution'); exit_with_success(array( 'buildRequests' => $resolve_id ? $requests_fetcher->results_with_resolved_ids() : $requests_fetcher->results(), 'commitSets' => $requests_fetcher->commit_sets(), 'commits' => $requests_fetcher->commits(), 'uploadedFiles' => $requests_fetcher->uploaded_files(), )); } function update_builds($db, $updates) { $db->begin_transaction(); $test_groups_may_need_more_requests = array(); foreach ($updates as $id => $info) { $id = intval($id); $status = $info['status']; $url = array_get($info, 'url'); $status_description = array_get($info, 'statusDescription'); $build_request_for_root_reuse_id = array_get($info, 'buildRequestForRootReuse'); $request_row = $db->select_first_row('build_requests', 'request', array('id' => $id)); $fields_to_update = array(); if (array_key_exists('url', $info)) $fields_to_update['url'] = $url; if (array_key_exists('statusDescription', $info)) $fields_to_update['status_description'] = $status_description; if ($status == 'failedIfNotCompleted') { if (!$request_row) { $db->rollback_transaction(); exit_with_error('FailedToFindBuildRequest', array('buildRequest' => $id)); } if ($request_row['request_status'] == 'completed') continue; $is_build = $request_row['request_order'] < 0; if ($is_build) { $db->query_and_fetch_all('UPDATE build_requests SET request_status = \'failed\' WHERE request_group = $1 AND request_order > $2', array($request_row['request_group'], $request_row['request_order'])); } $fields_to_update['status'] = 'failed'; $db->update_row('build_requests', 'request', array('id' => $id), $fields_to_update); } else { if (!in_array($status, array('pending', 'scheduled', 'running', 'failed', 'completed', 'canceled'))) { $db->rollback_transaction(); exit_with_error('UnknownBuildRequestStatus', array('buildRequest' => $id, 'status' => $status)); } $fields_to_update['status'] = $status; $db->update_row('build_requests', 'request', array('id' => $id), $fields_to_update); if ($build_request_for_root_reuse_id) { $build_request_for_root_reuse_id = intval($build_request_for_root_reuse_id); $build_request_for_root_reuse = $db->select_first_row('build_requests', 'request', array('id' => $build_request_for_root_reuse_id)); if (!$build_request_for_root_reuse) { $db->rollback_transaction(); exit_with_error('FailedToFindbuildRequestForRootReuse', array('buildRequest' => $build_request_for_root_reuse_id)); } if ($build_request_for_root_reuse['request_status'] != 'completed') { $db->rollback_transaction(); exit_with_error('CanOnlyReuseCompletedBuildRequest', array('buildRequest' => $build_request_for_root_reuse_id, 'status' => $build_request_for_root_reuse['request_status'])); } $error = reuse_roots_in_commit_set($db, $build_request_for_root_reuse['request_commit_set'], $request_row['request_commit_set']); if ($error) { $db->rollback_transaction(); exit_with_error($error['status'], $error['details']); } } if ($status != 'failed') continue; } $test_group_id = $request_row['request_group']; if (array_key_exists($test_group_id, $test_groups_may_need_more_requests)) continue; $db->update_row('analysis_test_groups', 'testgroup', array('id' => $test_group_id), array('may_need_more_requests' => TRUE)); $test_groups_may_need_more_requests[$test_group_id] = TRUE; } $db->commit_transaction(); } function reuse_roots_in_commit_set($db, $source_id, $destination_id) { $commit_set_items_source = $db->query_and_fetch_all('SELECT * FROM commit_set_items WHERE commitset_set = $1 AND commitset_commit IS NOT NULL', array($source_id)); $commit_set_items_destination = $db->query_and_fetch_all('SELECT * FROM commit_set_items WHERE commitset_set = $1 AND commitset_commit IS NOT NULL', array($destination_id)); if (count($commit_set_items_source) != count($commit_set_items_destination)) { return array('status' => 'CannotReuseRootWithNonMatchingCommitSets', 'details' => array('sourceCommitSet' => $source_id, 'destinationCommitSet' => $destination_id)); } $root_file_ids = array(); foreach ($commit_set_items_destination as &$destination_item) { $source_item = NULL; foreach ($commit_set_items_source as &$item) { if ($destination_item['commitset_commit'] == $item['commitset_commit'] && $destination_item['commitset_patch_file'] == $item['commitset_patch_file'] && $destination_item['commitset_commit_owner'] == $item['commitset_commit_owner'] && $destination_item['commitset_requires_build'] == $item['commitset_requires_build']) { $source_item = $item; break; } } if (!$source_item) return array('status' => 'NoMatchingCommitSetItem', 'details' => array('commitSet' => $source_id)); $root_file_id = $source_item['commitset_root_file']; if (!$root_file_id) { if (!$db->is_true($source_item['commitset_requires_build'])) continue; return array('status' => 'MissingRootFileFromSourceCommitSet', 'details' => array('commitSet' => $destination_id, 'rootFile' => $root_file_id)); } $root_file_row = $db->select_first_row('uploaded_files', 'file', array('id' => $root_file_id)); if ($root_file_row['file_deleted_at']) return array('status' => 'CannotReuseDeletedRoot', 'details' => array('commitSet' => $destination_id, 'rootFile' => $root_file_id)); $db->update_row('commit_set_items', 'commitset', array('set' => $destination_id, 'commit' => $destination_item['commitset_commit']), array('root_file' => $root_file_id), 'set'); array_push($root_file_ids, $root_file_id); } // There could be a race that those root files get purged after updating commit_set_items. // Add another round of check to ensure they are not deleted by the end of this function to // mitigate the race condition. foreach ($root_file_ids as &$root_file_id) { $root_file_row = $db->select_first_row('uploaded_files', 'file', array('id' => $root_file_id)); if ($root_file_row['file_deleted_at']) return array('status' => 'CannotReuseDeletedRoot', 'details' => array('commitSet' => $destination_id, 'rootFile' => $root_file_id)); } return NULL; } main(array_get($_GET, 'id'), array_key_exists('PATH_INFO', $_SERVER) ? explode('/', trim($_SERVER['PATH_INFO'], '/')) : array(), file_get_contents("php://input")); ?>