Search code examples
windowsbatch-filejenkinsjenkins-pipeline

Jenkins cuts off parameter list inside batch script


As part of my Jenkins pipeline I want to zip my output directory to a shared drive and I have a batch script that works wonderfully when running it through a shell, but when Jenkins runs it the file_names argument to 7zip goes missing and it zips the entire project folder instead.

The batch script looks as follows, where GGProjectName has a string value(no special characters) and CHANGESET is an integer

set path=D:\Data\Builds\%GGProjectName%\development

call "C:\Program Files\7-Zip\7z.exe" a -tzip %path%\%GGProjectName%-%CHANGESET%.zip .\Build\

Calling the batch script through a shell gives the following output with GGProjectName=ProjectName and CHANGESET=2:

E:\Jenkins\ProjectName\ProjectName_Main\workspace>set path=D:\Data\Builds\ProjectName\development

E:\Jenkins\ProjectName\ProjectName_Main\workspace>CALL "C:\Program Files\7-Zip\7z.exe" a -tzip D:\Data\Builds\ProjectName\development\ProjectName-2.zip E:\Jenkins\ProjectName\ProjectName_Main\workspace\Build\

7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04

Scanning the drive:
23 folders, 439 files, 718413046 bytes (686 MiB)

Creating archive: D:\Data\Builds\ProjectName\development\ProjectName-2.zip

Items to compress: 462


Files read from disk: 439
Archive size: 137063321 bytes (131 MiB)
Everything is Ok

Getting Jenkins to run the same script gives the following output, with GGProjectName=ProjectName and CHANGESET=77:

17:04:21  E:\Jenkins\ProjectName\ProjectName_Main\workspace>set path=D:\Data\Builds\ProjectName\development 
17:04:21  
17:04:21  E:\Jenkins\ProjectName\ProjectName_Main\workspace>CALL "C:\Program Files\7-Zip\7z.exe" a -tzip D:\Data\Builds\ProjectName\development\ProjectName-77 
17:05:57  
17:05:57  7-Zip [64] 16.04 : Copyright (c) 1999-2016 Igor Pavlov : 2016-10-04
17:05:57  
17:05:57  Scanning the drive:
17:05:57  856 folders, 14893 files, 2074486211 bytes (1979 MiB)
17:05:57  
17:05:57  Creating archive: D:\Data\Builds\ProjectName\development\ProjectName-77.zip
17:05:57  
17:05:57  Items to compress: 15749
17:05:57  
17:05:57  
17:05:57  Files read from disk: 14893
17:05:57  Archive size: 453305828 bytes (433 MiB)
17:05:57  Everything is Ok

I've identified the issue as Jenkins somehow cutting off the last argument to the call to 7z.exe, though I have no idea what causes it.

The Jenkins stage that runs the script is extremely straightforward so I have a hard time seeing that as the cause.

stage('Deploy') {
    steps {
        bat 'publish.bat'
    }
}

I'm running Jenkins 2.204.2 on a Windows machine.


Solution

  • The main problem is most likely the string value assigned to environment variable CHANGESET which is not just the string 77, but the string 77 with a carriage return or a line-feed or carriage return + line-feed appended which results in a truncated command line on execution by Jenkins. So the source of this problem must be searched in code which defines the environment variable CHANGESET with a string not being only the number, but the number with a newline character.

    GG_Victor wrote in a comment:

    The carriage return seems to have been the issue and changing the assignment of CHANGESET to have a .trim() on the shell output in the jenkinsfile like so: env.CHANGESET = sh (...).trim() alleviated the issue.

    But it would be even better to find out why the environment variable CHANGESET is defined with a string value with a carriage return appended to the number as that is the real source of the issue.


    Another issue with the batch code is the redefinition of the predefined environment variable PATH with a string being a single folder path. Please take a look on What is the reason for "X is not recognized as an internal or external command, operable program or batch file"? You should know after reading the long answer from top to bottom why path should not be used as name of a local environment variable. Better would be FolderPath or ProjectPath or ArchivePath instead of path.

    So the first line should be for example:

    set "ArchivePath=D:\Data\Builds\%GGProjectName%\development"
    

    The usage of " as done here is highly recommended. The reason is described in my answer on:
    Why is no string output with 'echo %var%' after using 'set var = text' on command line?


    The command CALL is needed only to call a batch file from within a batch file. It is not needed to run an executable from within a batch file. The Windows command processor halts the batch file execution until the started executable terminated itself. So the batch file execution is in your case not continued by cmd.exe until the started 7z.exe finished creating the archive file and terminated with exit code returned back to Windows command processor which assigns it to ERRORLEVEL for evaluation by one of the next commands.

    This behavior on starting a console or GUI executable from within a batch file can be seen easily by creating a batch file with following five lines and execute it with a double click:

    @echo off
    echo Run Notepad.
    %SystemRoot%\Notepad.exe
    echo Notepad terminated.
    pause
    

    The Windows command processor outputs Run Notepad. and starts next Windows Notepad. The console window shows just Run Notepad. as long as the graphical window of Notepad is open because of cmd.exe waits for termination of Windows Notepad. Once Windows Notepad is exited by the user, the Windows command processor continues and outputs next the line Notepad terminated. Last the command execution is paused once more because of command PAUSE to give the user the possibility to see that the batch file execution continued after Notepad termination.

    This behavior on starting an executable from within a batch file is different to starting an executable from within a command prompt window. Windows command processor does not wait for termination of the started executable if a user opens a command prompt, enters %SystemRoot%\Notepad.exe and hits key RETURN or ENTER to run Windows Notepad. The user can in this case immediately enter and execute the next command after switching from still opened Notepad window to the Windows command prompt window.

    So for the second line should be used:

    "C:\Program Files\7-Zip\7z.exe" a -bd -tzip -y -- "%ArchivePath%\%GGProjectName%-%CHANGESET%.zip" ".\Build\"
    

    The double quotes are highly recommended again, especially on full qualified archive file name containing - after a space to avoid getting the rest interpreted by mistake as switch by 7z.exe or on full qualified file name containing a space or one of these characters &()[]{}^=;!'+,`~. It is best to always enclose file/folder names in double quotes even on not being really necessary.

    The three additional switches -bd, -y and -- are described in help of 7-Zip.