Search code examples
javadockergitlab-cigitlab-ci-runner

gitlab-runner passes wrong arguments to custom image


I have written a small CLI using Java, Argparse4j, and packaged it in docker using this Dockerfile:

FROM openjdk:18

ENV JAR_NAME "my-jar-with-dependencies.jar"

ENV PROJECT_HOME /opt/app
RUN mkdir -p $PROJECT_HOME

WORKDIR $PROJECT_HOME
COPY run.sh $PROJECT_HOME/run.sh
RUN chmod +x $PROJECT_HOME/run.sh

COPY target/$JAR_NAME $PROJECT_HOME/cli.jar

ENTRYPOINT ["./run.sh"]

The last line of the Dockerfile then invokes a simple bash script:

#!/bin/bash

java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ./cli.jar "$@"

The CLI I wrote has three main actions: upload, download and apply. Therefore argparse4j expects one of these actions to be passed as the first parameter, i.e.

java -jar cli.jar download #... whatever other argument

This works just fine when running the docker image locally, but completely fails when running in the CI pipeline:

download:
  stage: download
  image: <url>/my-image:<tag>
  variables:
    URL: <URL>
    API_KEY: <API_KEY>
    CI_DEBUG_TRACE: "true"
  script:
    - download -f zip -u true test-download.zip

This is the error that is returned:

Executing "step_script" stage of the job script 00:01
Using docker image sha256:<sha> for <url>/my-image:<tag> with digest <url>/my-image:<tag>@sha256:<sha> ...
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
usage: tool [-h] ACTION ...
tool: error:  invalid  choice:  'sh'  (choose  from  'upload',  'download',
'apply')

I have tried following the suggestion in gitlab-runner doesn't run ENTRYPOINT scripts in Dockerfile but I can't seem to get the CI part to work correctly.

I would like to avoid using the entrypoint directive as it needs to be used on multiple files, so I rather fix the issue at the root.

Does anyone have an idea of what is happening or how to fix it?


Solution

  • So, after a bit of research, I have been able to find a solution that works for me.

    From my research (and as Pierre B. pointed out in his answer), Gitlab essentially tries to inject a shell script that performs a check for which shell is available.

    Now, my solution is in no way elegant, but does achieve what I wanted. I modified the Dockerfile like so:

    FROM openjdk:18-bullseye
    
    ENV JAR_NAME "my-jar.jar"
    
    ENV PROJECT_HOME /opt/app
    RUN mkdir -p $PROJECT_HOME
    
    WORKDIR $PROJECT_HOME
    COPY run.sh $PROJECT_HOME/run.sh
    RUN chmod +x $PROJECT_HOME/run.sh
    
    COPY target/$JAR_NAME $PROJECT_HOME/cli.jar
    
    RUN  echo '#!/bin/bash \njava $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar $PROJECT_HOME/cli.jar "$@"' > /usr/bin/i18n &&  \
         chmod +x /usr/bin/i18n
    
    ENTRYPOINT ["./run.sh"]
    

    And also modified the run.sh script this way:

    #!/bin/bash
    
    if [[ -n "$CI" ]]; then
        echo "this block will only execute in a CI environment"
        exec /bin/bash
    else
        echo "Not in CI. Running the image normally"
        java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar ./cli.jar "$@"
    fi
    

    This works because Gitlab, in its list of predefined variables, provides a CI env var that is set when the script is running on the CI. By doing so, I skip the java invocation but leave it in the case I need to use it when not on a CI.

    Now when I need to use my image, all I need to specify in my .gitlab-ci.yml file is this:

    download:
      stage: download
      image: <url>/my-image:<tag>
      variables:
        URL: <URL>
        API_KEY: <API_KEY>
        CI_DEBUG_TRACE: "true"
      script:
        - i18n download -f zip -u true test-download.zip # This calls the script that was injected in the Dockerfile
    

    This way I essentially mimic an actual CLI, and can use it in all my projects that require this tool.

    I am not sure though why I need to "echo" the script for the CLI, and I can't simply copy it. For some reason the env variables are not passed down and I couldn't spend any more time debugging it. So for now, it will stay like this.

    If you have any recommendations on how to clean this up, please leave some comments and I will edit my answer!