static async gather(gatherFn, options) {
    const settings = options.resolvedConfig.settings;

    // Either load saved artifacts from disk or from the browser.
    try {
      const sentryContext = Sentry.getContext();
      Sentry.captureBreadcrumb({
        message: 'Run started',
        category: 'lifecycle',
        data: sentryContext,
      });

      if (settings.auditMode && !settings.gatherMode) {
        // No browser required, just load the artifacts from disk.
        const path = this._getDataSavePath(settings);
        return assetSaver.loadArtifacts(path);
      }

      const runnerStatus = {msg: 'Gather phase', id: 'lh:runner:gather'};
      log.time(runnerStatus, 'verbose');

      const artifacts = await gatherFn({resolvedConfig: options.resolvedConfig});
      log.timeEnd(runnerStatus);

      // If `gather` is run multiple times before `audit`, the timing entries for each `gather` can pollute one another.
      // We need to clear the timing entries at the end of gathering.
      // Set artifacts.Timing again to ensure lh:runner:gather is included.
      artifacts.Timing = log.takeTimeEntries();

      // -G means save these to disk (e.g. ./latest-run).
      if (settings.gatherMode) {
        const path = this._getDataSavePath(settings);
        await assetSaver.saveArtifacts(artifacts, path);
      }

      return artifacts;
    } catch (err) {
      throw Runner.createRunnerError(err, settings);
    }
  }
User can run -G solo, -A solo, or -GA together
-G and -A will run partial lighthouse pipelines,
and -GA will run everything plus save artifacts and lhr to disk.

@param {(runnerData: {resolvedConfig: LH.Config.ResolvedConfig}) => Promise<LH.Artifacts>} gatherFn
@param {{resolvedConfig: LH.Config.ResolvedConfig, computedCache: Map<string, ArbitraryEqualityMap>}} options
@return {Promise<LH.Artifacts>}