@VisibleForTesting
  static Optional<String> performUpdateCheck(
      Path configDir,
      String currentVersion,
      String versionUrl,
      String toolName,
      Consumer<LogEvent> log) {
    Path lastUpdateCheck = configDir.resolve(LAST_UPDATE_CHECK_FILENAME);

    try {
      // Check time of last update check
      if (Files.exists(lastUpdateCheck)) {
        try {
          String fileContents =
              new String(Files.readAllBytes(lastUpdateCheck), StandardCharsets.UTF_8);
          Instant modifiedTime = Instant.parse(fileContents);
          if (modifiedTime.plus(Duration.ofDays(1)).isAfter(Instant.now())) {
            return Optional.empty();
          }
        } catch (DateTimeParseException | IOException ex) {
          // If reading update time failed, file might be corrupt, so delete it
          log.accept(LogEvent.debug("Failed to read lastUpdateCheck; " + ex.getMessage()));
          Files.delete(lastUpdateCheck);
        }
      }

      // Check for update
      FailoverHttpClient httpClient = new FailoverHttpClient(true, false, ignored -> {});
      try {
        Response response =
            httpClient.get(
                new URL(versionUrl),
                Request.builder()
                    .setHttpTimeout(3000)
                    .setUserAgent("jib " + currentVersion + " " + toolName)
                    .build());
        VersionJsonTemplate version =
            JsonTemplateMapper.readJson(response.getBody(), VersionJsonTemplate.class);

        Path lastUpdateCheckTemp =
            Files.createTempFile(configDir, LAST_UPDATE_CHECK_FILENAME, null);
        lastUpdateCheckTemp.toFile().deleteOnExit();
        Files.write(lastUpdateCheckTemp, Instant.now().toString().getBytes(StandardCharsets.UTF_8));
        Files.move(lastUpdateCheckTemp, lastUpdateCheck, StandardCopyOption.REPLACE_EXISTING);

        if (currentVersion.equals(version.latest)) {
          return Optional.empty();
        }
        return Optional.of(version.latest);
      } finally {
        httpClient.shutDown();
      }

    } catch (IOException ex) {
      log.accept(LogEvent.debug("Update check failed; " + ex.getMessage()));
    }

    return Optional.empty();
  }
@Test
  public void testPerformUpdateCheck_badLastUpdateTime() throws IOException, InterruptedException {
    Instant before = Instant.now();
    Thread.sleep(100);
    Files.write(
        configDir.resolve("lastUpdateCheck"), "bad timestamp".getBytes(StandardCharsets.UTF_8));
    Optional<String> message =
        UpdateChecker.performUpdateCheck(
            configDir, "1.0.2", testWebServer.getEndpoint(), "tool-name", ignored -> {});
    String modifiedTime =
        new String(
            Files.readAllBytes(configDir.resolve("lastUpdateCheck")), StandardCharsets.UTF_8);

    assertThat(Instant.parse(modifiedTime)).isGreaterThan(before);
    assertThat(message).hasValue("2.0.0");
  }