美文网首页
sonarqube+sonar-scanner-engine扫描

sonarqube+sonar-scanner-engine扫描

作者: 空_7788 | 来源:发表于2019-03-25 21:04 被阅读0次

    一. 获取插件并加载

    1. 获取已有插件列表

    请求/api/plugins/installed获取已安装的插件
    org.sonar.scanner.bootstrap.ScannerPluginInstaller

    InstalledPlugin[] listInstalledPlugins() {
        Profiler profiler = Profiler.create(LOG).startInfo("Load plugins index");
        GetRequest getRequest = new GetRequest(PLUGINS_WS_URL);
        InstalledPlugins installedPlugins;
        try (Reader reader = wsClient.call(getRequest).contentReader()) {
          installedPlugins = new Gson().fromJson(reader, InstalledPlugins.class);
        } catch (IOException e) {
          throw new IllegalStateException(e);
        }
    
        profiler.stopInfo();
        return installedPlugins.plugins;
      }
    

    2. 下载插件jar包

    org.sonar.scanner.bootstrap.ScannerPluginInstaller

    private Map<String, ScannerPlugin> loadPlugins(InstalledPlugin[] remotePlugins) {
        Map<String, ScannerPlugin> infosByKey = new HashMap<>(remotePlugins.length);
    
        Profiler profiler = Profiler.create(LOG).startDebug("Load plugins");
    
        for (InstalledPlugin installedPlugin : remotePlugins) {
          if (pluginPredicate.apply(installedPlugin.key)) {
            File jarFile = download(installedPlugin);
            PluginInfo info = PluginInfo.create(jarFile);
            infosByKey.put(info.getKey(), new ScannerPlugin(installedPlugin.key, installedPlugin.updatedAt, info));
          }
        }
    
        profiler.stopDebug();
        return infosByKey;
      }
    

    3. 加载插件

    org.sonar.core.platform.PluginLoader

    Map<String, Plugin> instantiatePluginClasses(Map<PluginClassLoaderDef, ClassLoader> classloaders) {
        // instantiate plugins
        Map<String, Plugin> instancesByPluginKey = new HashMap<>();
        for (Map.Entry<PluginClassLoaderDef, ClassLoader> entry : classloaders.entrySet()) {
          PluginClassLoaderDef def = entry.getKey();
          ClassLoader classLoader = entry.getValue();
    
          // the same classloader can be used by multiple plugins
          for (Map.Entry<String, String> mainClassEntry : def.getMainClassesByPluginKey().entrySet()) {
            String pluginKey = mainClassEntry.getKey();
            String mainClass = mainClassEntry.getValue();
            try {
              instancesByPluginKey.put(pluginKey, (Plugin) classLoader.loadClass(mainClass).newInstance());
            } catch (UnsupportedClassVersionError e) {
              throw new IllegalStateException(String.format("The plugin [%s] does not support Java %s",
                pluginKey, SystemUtils.JAVA_VERSION_TRIMMED), e);
            } catch (Throwable e) {
              throw new IllegalStateException(String.format(
                "Fail to instantiate class [%s] of plugin [%s]", mainClass, pluginKey), e);
            }
          }
        }
        return instancesByPluginKey;
      }
    

    4. 加载插件中所有的扩展类

    org.sonar.scanner.bootstrap.ExtensionInstaller

    public ExtensionInstaller install(ComponentContainer container, ExtensionMatcher matcher) {
    
        // core components
        for (Object o : BatchComponents.all(analysisMode)) {
          doInstall(container, matcher, null, o);
        }
    
        // plugin extensions
        for (PluginInfo pluginInfo : pluginRepository.getPluginInfos()) {
          Plugin plugin = pluginRepository.getPluginInstance(pluginInfo.getKey());
          Plugin.Context context = new Plugin.Context(sonarRuntime);
          plugin.define(context);
          for (Object extension : context.getExtensions()) {
            doInstall(container, matcher, pluginInfo, extension);
          }
        }
        List<ExtensionProvider> providers = container.getComponentsByType(ExtensionProvider.class);
        for (ExtensionProvider provider : providers) {
          Object object = provider.provide();
          if (object instanceof Iterable) {
            for (Object extension : (Iterable) object) {
              doInstall(container, matcher, null, extension);
            }
          } else {
            doInstall(container, matcher, null, object);
          }
        }
        return this;
      }
    

    二. 获取规则配置和可用规则

    1. 获取规则配置

    请求/api/qualityprofiles/search获取规则配置
    org.sonar.scanner.repository.DefaultQualityProfileLoader

    private List<QualityProfile> loadAndOverrideIfNeeded(@Nullable String profileName, StringBuilder url) {
        getOrganizationKey().ifPresent(k -> url.append("&organization=").append(encodeForUrl(k)));
        Map<String, QualityProfile> result = call(url.toString());
    
        if (profileName != null) {
          StringBuilder urlForName = new StringBuilder(WS_URL + "?profileName=");
          urlForName.append(encodeForUrl(profileName));
          getOrganizationKey().ifPresent(k -> urlForName.append("&organization=").append(encodeForUrl(k)));
          result.putAll(call(urlForName.toString()));
        }
        if (result.isEmpty()) {
          throw MessageException.of("No quality profiles have been found, you probably don't have any language plugin installed.");
        }
    
        return new ArrayList<>(result.values());
      }
    

    2. 获取每个规则配置下可用的规则

    请求/api/rules/search获取每个规则配置下对应的可用的规则
    org.sonar.scanner.rule.DefaultActiveRulesLoader

    public List<LoadedActiveRule> load(String qualityProfileKey) {
        List<LoadedActiveRule> ruleList = new LinkedList<>();
        int page = 1;
        int pageSize = 500;
        int loaded = 0;
    
        while (true) {
          GetRequest getRequest = new GetRequest(getUrl(qualityProfileKey, page, pageSize));
          SearchResponse response = loadFromStream(wsClient.call(getRequest).contentStream());
          List<LoadedActiveRule> pageRules = readPage(response);
          ruleList.addAll(pageRules);
          loaded += response.getPs();
    
          if (response.getTotal() <= loaded) {
            break;
          }
          page++;
        }
    
        return ruleList;
      }
    

    三. 执行插件中的规则

    1. 获取所有插件的执行类并实例化

    从插件扩展类中查找实现org.sonar.api.batch.sensor.Sensor接口的执行类
    org.sonar.scanner.bootstrap.ScannerExtensionDictionnary

    private <T> List<T> getFilteredExtensions(Class<T> type, @Nullable DefaultInputModule module, @Nullable ExtensionMatcher matcher) {
        List<T> result = new ArrayList<>();
        List<Object> candidates = new ArrayList<>();
        candidates.addAll(getExtensions(type));
        if (org.sonar.api.batch.Sensor.class.equals(type)) {
          candidates.addAll(getExtensions(Sensor.class));
        }
        if (org.sonar.api.batch.PostJob.class.equals(type)) {
          candidates.addAll(getExtensions(PostJob.class));
        }
    
        for (Object extension : candidates) {
          if (org.sonar.api.batch.Sensor.class.equals(type) && extension instanceof Sensor) {
            extension = new SensorWrapper((Sensor) extension, sensorContext, sensorOptimizer);
          }
          if (org.sonar.api.batch.PostJob.class.equals(type) && extension instanceof PostJob) {
            extension = new PostJobWrapper((PostJob) extension, postJobContext, postJobOptimizer);
          }
          if (shouldKeep(type, extension, module, matcher)) {
            result.add((T) extension);
          }
        }
        return result;
      }
    
    使用Pico容器,实例化暂时没具体研究

    2. 带入可用规则执行插件的扫描方法

    org.sonar.scanner.sensor.SensorWrapper

    @Override
      public void analyse(Project module, org.sonar.api.batch.SensorContext context) {
        wrappedSensor.execute(adaptor);
      }
    

    四. 问题记录和报告上传(java为例)

    1. 问题记录

    问题记录写入pb文件
    org.sonar.java.SonarComponents

    void reportIssue(AnalyzerMessage analyzerMessage, RuleKey key, InputPath inputPath, Double cost) {
        Preconditions.checkNotNull(context);
        JavaIssue issue = JavaIssue.create(context, key, cost);
        AnalyzerMessage.TextSpan textSpan = analyzerMessage.primaryLocation();
        if (textSpan == null) {
          // either an issue at file or folder level
          issue.setPrimaryLocationOnFile(inputPath, analyzerMessage.getMessage());
        } else {
          if (!textSpan.onLine()) {
            Preconditions.checkState(!textSpan.isEmpty(), "Issue location should not be empty");
          }
          issue.setPrimaryLocation((InputFile) inputPath, analyzerMessage.getMessage(), textSpan.startLine, textSpan.startCharacter, textSpan.endLine, textSpan.endCharacter);
        }
        issue.addFlow(inputFromIOFile(analyzerMessage.getFile()), analyzerMessage.flows).save();
      }
    

    org.sonar.java.JavaIssue

    public void save() {
        newIssue.save();
      }
    

    ......
    org.sonar.scanner.protocol.output.ScannerReportWriter

    public void appendComponentIssue(int componentRef, ScannerReport.Issue issue) {
        File file = fileStructure.fileFor(FileStructure.Domain.ISSUES, componentRef);
        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true))) {
          issue.writeDelimitedTo(out);
        } catch (Exception e) {
          throw ContextException.of("Unable to write issue", e).addContext("file", file);
        }
      }
    

    2. 报告上传

    压缩为zip包
    org.sonar.scanner.report.ReportPublisher

    private File generateReportFile() {
        try {
          long startTime = System.currentTimeMillis();
          for (ReportPublisherStep publisher : publishers) {
            publisher.publish(writer);
          }
          long stopTime = System.currentTimeMillis();
          LOG.info("Analysis report generated in {}ms, dir size={}", stopTime - startTime, FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(reportDir.toFile())));
    
          startTime = System.currentTimeMillis();
          File reportZip = temp.newFile("scanner-report", ".zip");
          ZipUtils.zipDir(reportDir.toFile(), reportZip);
          stopTime = System.currentTimeMillis();
          LOG.info("Analysis reports compressed in {}ms, zip size={}", stopTime - startTime, FileUtils.byteCountToDisplaySize(FileUtils.sizeOf(reportZip)));
          return reportZip;
        } catch (IOException e) {
          throw new IllegalStateException("Unable to prepare analysis report", e);
        }
      }
    

    以application/x-protobuf协议上传

    String upload(File report) {
        LOG.debug("Upload report");
        long startTime = System.currentTimeMillis();
        PostRequest.Part filePart = new PostRequest.Part(MediaTypes.ZIP, report);
        PostRequest post = new PostRequest("api/ce/submit")
          .setMediaType(MediaTypes.PROTOBUF)
          .setParam("organization", settings.get(ORGANIZATION).orElse(null))
          .setParam("projectKey", moduleHierarchy.root().key())
          .setParam("projectName", moduleHierarchy.root().getOriginalName())
          .setParam("projectBranch", moduleHierarchy.root().getBranch())
          .setPart("report", filePart);
    
        String branchName = branchConfiguration.branchName();
        if (branchName != null) {
          post.setParam(CHARACTERISTIC, "branch=" + branchName);
          post.setParam(CHARACTERISTIC, "branchType=" + branchConfiguration.branchType().name());
        }
    
        WsResponse response;
        try {
          response = wsClient.call(post).failIfNotSuccessful();
        } catch (HttpException e) {
          throw MessageException.of(String.format("Failed to upload report - %d: %s", e.code(), ScannerWsClient.tryParseAsJsonError(e.content())));
        }
    
        try (InputStream protobuf = response.contentStream()) {
          return WsCe.SubmitResponse.parser().parseFrom(protobuf).getTaskId();
        } catch (Exception e) {
          throw Throwables.propagate(e);
        } finally {
          long stopTime = System.currentTimeMillis();
          LOG.info("Analysis report uploaded in " + (stopTime - startTime) + "ms");
        }
      }
    

    相关文章

      网友评论

          本文标题:sonarqube+sonar-scanner-engine扫描

          本文链接:https://www.haomeiwen.com/subject/mjkivqtx.html