javabashgithub-actionssonarqube-scan

How can I catch a Java exception in Github Action


I've got a Github Action that runs SonarQube scanner:

- name: SonarQube analysis
  continue-on-error: true
  shell: bash
  run: |
    sonar-scanner \
      -Dsonar.cfamily.build-wrapper-output=bw-output
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
    SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}

Mostly this works fine, but sometimes it throws an exception because of a hash/checksum error on a downloaded file. I can rerun the action and it'll usually work but because the build takes a while I'd rather rerun just this step.

I tried using Retrying a bash command - Meziantou's blog and removing the continue-on-error: true command:

- name: SonarQube analysis
  shell: bash
  run: |
    # Set the maximum number of attempts
    max_attempts=5
    # Set a counter for the number of attempts
    attempt_num=1
    # Set a flag to indicate whether the command was successful
    success=false
    # Loop until the command is successful or the maximum number of attempts is reached
    while [ $success = false ] && [ $attempt_num -le $max_attempts ]; do
      # Execute the command
      sonar-scanner \
        -Dsonar.cfamily.build-wrapper-output=bw-output
      # Check the exit code of the command
      if [ $? -eq 0 ]; then
        # The command was successful
        success=true
      else
        # The command was not successful
        echo "Attempt $attempt_num failed. Trying again..."
        # Increment the attempt counter
        attempt_num=$(( attempt_num + 1 ))
      fi
    done
    # Check if the command was successful
    if [ $success = true ]; then
      # The command was successful
      echo "sonar-scanner was successful after $attempt_num attempts."
    else
      # The command was not successful
      echo "sonar-scanner failed after $max_attempts attempts."
    fi
  env:
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
    SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}

but it doesn't work - when an exception is thrown it exits the loop:

03:33:30.088 INFO  Scanner configuration file: /home/runner/.sonar/sonar-scanner-6.2.1.4610-linux-x64/conf/sonar-scanner.properties
03:33:30.102 INFO  SonarScanner CLI 6.2.1.4610
03:33:30.104 INFO  Java 17.0.12 Eclipse Adoptium (64-bit)
03:33:30.104 INFO  Linux 6.5.0-1025-azure amd64
03:33:30.125 INFO  User cache: /home/runner/.sonar/cache
03:33:53.701 INFO  Communicating with SonarQube Server 9.9.7.96285
03:33:53.964 INFO  Load global settings
03:33:54.399 INFO  Load global settings (done) | time=435ms
03:33:54.401 INFO  Server id: 8F6C7167-AW8zf1mS7d4EVV8efcCG
03:33:54.404 INFO  User cache: /home/runner/.sonar/cache
03:33:54.406 INFO  Load/download plugins
03:33:54.407 INFO  Load plugins index
03:33:54.794 INFO  Load plugins index (done) | time=387ms
03:34:20.190 INFO  Load/download plugins (done) | time=25784ms
03:34:20.198 INFO  EXECUTION FAILURE
03:34:20.199 INFO  Total time: 50.113s
03:34:20.199 ERROR Error during SonarScanner CLI execution
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.sonarsource.scanner.lib.internal.IsolatedClassloader@740d2e78-org.sonar.scanner.bootstrap.ScannerPluginRepository': Initialization of bean failed; nested exception is java.lang.IllegalStateException: Fail to download plugin [cpp]. File /home/runner/.sonar/_tmp/fileCache2060103996137565963.tmp was expected to have checksum 648b7fda1ced5cc3a204981786143349 but had 5c56ae7029503f40ef4e337edf513626
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:628)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
    at org.sonar.core.platform.SpringComponentContainer.startComponents(SpringComponentContainer.java:187)
    at org.sonar.core.platform.SpringComponentContainer.execute(SpringComponentContainer.java:167)
    at org.sonar.batch.bootstrapper.Batch.doExecute(Batch.java:72)
    at org.sonar.batch.bootstrapper.Batch.execute(Batch.java:66)
    at org.sonarsource.scanner.lib.internal.batch.BatchIsolatedLauncher.execute(BatchIsolatedLauncher.java:41)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.base/java.lang.reflect.Method.invoke(Unknown Source)
    at org.sonarsource.scanner.lib.internal.IsolatedLauncherProxy.invoke(IsolatedLauncherProxy.java:62)
    at jdk.proxy3/jdk.proxy3.$Proxy2.execute(Unknown Source)
    at org.sonarsource.scanner.lib.InProcessScannerEngineFacade.doAnalyze(InProcessScannerEngineFacade.java:39)
    at org.sonarsource.scanner.lib.ScannerEngineFacade.analyze(ScannerEngineFacade.java:61)
    at org.sonarsource.scanner.cli.Main.analyze(Main.java:77)
    at org.sonarsource.scanner.cli.Main.main(Main.java:63)
Caused by: java.lang.IllegalStateException: Fail to download plugin [cpp]. File /home/runner/.sonar/_tmp/fileCache2060103996137565963.tmp was expected to have checksum 648b7fda1ced5cc3a204981786143349 but had 5c56ae7029503f40ef4e337edf513626
    at org.sonar.scanner.bootstrap.PluginFiles.download(PluginFiles.java:105)
    at org.sonar.scanner.bootstrap.PluginFiles.get(PluginFiles.java:82)
    at org.sonar.scanner.bootstrap.ScannerPluginInstaller.loadPlugins(ScannerPluginInstaller.java:78)
    at org.sonar.scanner.bootstrap.ScannerPluginInstaller.installRemotes(ScannerPluginInstaller.java:61)
    at org.sonar.scanner.bootstrap.ScannerPluginRepository.start(ScannerPluginRepository.java:63)
    at org.sonar.core.platform.StartableBeanPostProcessor.postProcessBeforeInitialization(StartableBeanPostProcessor.java:33)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
    ... 23 common frames omitted
03:34:20.200 ERROR 
03:34:20.200 ERROR Re-run SonarScanner CLI using the -X switch to enable full debug logging.
Error: Process completed with exit code 1.

How can I get this step to repeat if it fails? Note I'd prefer to do this without a thirdparty solution because that would require approval from our security team.


Solution

  • Setting shell: bash results in this command being run internally:

    bash --noprofile --norc -eo pipefail {0}
    

    (see documentation).

    It includes -e, "error on exit", so when your scanner hits an error, the entire step is aborted.

    You can prevent that from happening by executing the command in an if-clause, which exempts it from the set -e effects:

    # Execute the command
    if sonar-scanner -Dsonar.cfamily.build-wrapper-output=bw-output; then
        success=true
    else
        # The command was not successful
    

    As a side note, there is a dedicated SonarQube action provided by SonarSource; using it might simplify your setup.