Skip to content

Commit c29a041

Browse files
committed
Ignore ancient builds with higher safe levels
When searching for the latest build on a branch for a particular safe level, it is possible to encounter very old builds. This happens if a build used to have more validation, and it since has dropped running that validation. To find the combination of "latest" and "most safe", the fetcher will now discard builds from consideration if they are more than a week older than the latest build at any safe level. Bug: b/524908123 Test: Use build example build from b/524908123#comment19
1 parent b545852 commit c29a041

1 file changed

Lines changed: 45 additions & 3 deletions

File tree

base/cvd/cuttlefish/host/libs/web/android_build_api.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616
#include "cuttlefish/host/libs/web/android_build_api.h"
1717

1818
#include <dirent.h>
19+
#include <time.h>
1920
#include <unistd.h>
2021

22+
#include <algorithm>
2123
#include <chrono>
24+
#include <iomanip>
2225
#include <memory>
2326
#include <optional>
27+
#include <ranges>
2428
#include <set>
29+
#include <sstream>
2530
#include <string>
2631
#include <string_view>
2732
#include <thread>
@@ -261,8 +266,24 @@ Result<std::vector<std::string>> AndroidBuildApi::Headers() {
261266
return headers;
262267
}
263268

269+
static Result<std::chrono::system_clock::time_point> ParseTime(
270+
std::string_view time_str) {
271+
std::stringstream stream = std::stringstream(std::string(time_str));
272+
tm time_tm;
273+
stream >> std::get_time(&time_tm, "%Y-%m-%dT%H:%M:%S.");
274+
CF_EXPECTF(!!stream, "Failed to parse time '{}'", time_str);
275+
276+
return std::chrono::system_clock::from_time_t(mktime(&time_tm));
277+
}
278+
264279
Result<std::optional<std::string>> AndroidBuildApi::LatestBuildId(
265280
const std::string& branch, const std::string& target) {
281+
struct CandidateBuild {
282+
std::string build_id;
283+
std::chrono::system_clock::time_point creation_time;
284+
};
285+
// Find the latest build at every safe level
286+
std::vector<CandidateBuild> candidates;
266287
for (const SafeLevel safe_level : kAllSafeLevels) {
267288
VLOG(0) << "Attempting to download build at safe level '" << safe_level
268289
<< "' for branch '" << branch << "' and target '" << target << "'";
@@ -276,7 +297,7 @@ Result<std::optional<std::string>> AndroidBuildApi::LatestBuildId(
276297
if (!json_res.ok()) {
277298
continue;
278299
}
279-
const Json::Value json = *json_res;
300+
const Json::Value& json = *json_res;
280301

281302
if (!json.isMember("builds")) {
282303
continue;
@@ -287,9 +308,30 @@ Result<std::optional<std::string>> AndroidBuildApi::LatestBuildId(
287308
"target \"{}\" in the response array, "
288309
"but found {}",
289310
branch, target, json["builds"].size());
290-
return CF_EXPECT(GetValue<std::string>(json["builds"][0], {"buildId"}));
311+
312+
const Json::Value& build = json["builds"][0];
313+
const std::string& completion = build["completionTimestamp"].asString();
314+
candidates.emplace_back(CandidateBuild{
315+
.build_id = build["buildId"].asString(),
316+
.creation_time = CF_EXPECT(ParseTime(completion)),
317+
});
318+
}
319+
if (candidates.empty()) {
320+
return std::nullopt;
291321
}
292-
return std::nullopt;
322+
// Drop candidate builds older than 1 week
323+
auto creation = [](const auto& cd) { return cd.creation_time; };
324+
std::chrono::system_clock::time_point latest =
325+
std::ranges::max(std::views::transform(candidates, creation));
326+
// NOLINTNEXTLINE(misc-include-cleaner): <chrono> provides std::chrono::weeks
327+
static constexpr std::chrono::weeks kWeek = std::chrono::weeks(1);
328+
auto recent = [latest](const auto& cd) {
329+
return latest - cd.creation_time < kWeek;
330+
};
331+
auto recent_candidates = std::views::filter(candidates, recent);
332+
CF_EXPECT(!std::ranges::empty(recent_candidates));
333+
// Return the first valid build (which will have the highest safe level)
334+
return recent_candidates.begin()->build_id;
293335
}
294336

295337
Result<std::unordered_set<std::string>> AndroidBuildApi::Artifacts(

0 commit comments

Comments
 (0)