Shell podmínky v Gitlab CI

Git, Linux

Skripty v CI/CD pipeline mohou obsahovat i podmínky a je více možností, jak toto vyřešit. Jenže jaký shell je použit a jakou zvolit syntaxi? Toto není nikde v Gitlab dokumentaci dobře popsáno.

Shell podmínky v Gitlab CI

Article in English can be found on dev.to/arxeiss/shell-conditions-in-gitlab-ci-545

Tento článek předpokládá, že CI executor je nastaven na Docker. Což je defaultní nastavení pro sdílené runnery. S vlastním runnerem lze využít přímo Shell na hostitelském PC.

Při vytvoření .gitlab-ci.yml souboru se definuje Docker image, který se spustí, a následně příkazy, které se provádí. Tyto příkazy ale mohou obsahovat podmínky či jiné konstrukce a každý Shell může vyžadovat či podporovat jinou syntaxi. A pokud použitý Docker image obsahuje více různých Shell implementací, je potřeba zjistit, který si Gitlab Runner vybere jako ten hlavní.

Určení Shellu s externím souborem

Pokud skripty nejsou zapsány přímo v .gitlab-ci.yml souboru, ale v externím souboru, stačí definovat cestu k Shellu, cestu k souboru a je hotovo. Pak je úplně jedno, jaký hlavní Shell si Gitlab Runner vybere, protože se vždy spustí zde vybraný /bin/bash. Pipeline pak může vypadat takto:

deploy:
  image: registry.gitlab.com/pavel.kutac/docker-ftp-deployer:php81
  script:
    - /bin/bash /usr/local/bin/execute-deployment.sh

Podmínky přímo v .gitlab-ci.yml souboru

Podmínky lze zapsat přímo do YAML souboru ideálně pomocí Literal block style konstrukce. Příklad takové pipeline může být následující. Použité skripty a image je z Docker FTP Deployeru.

deploy:
  image: registry.gitlab.com/pavel.kutac/docker-ftp-deployer:php81
  script: |
    if [[ ${CI_COMMIT_MESSAGE,,} != "[skip-maintenance]"* ]]; then
      wget --no-verbose -O - https://kutac.cz/some/path/to/turn/on/maintenance-mode
    fi
    ./.ci/deploy/run-lftp-incremental-sync.sh

Jenže jak zjistit, jaký Shell bude tyto skripty vykonávat? Zmíněný image obsahuje Busybox sh ale také bash. A tuto informaci nelze dohledat v dokumentaci. Proto jsem prošel zdrojové kódy Gitlab Runneru, kde jsem našel kousek kódu zobrazeného níže.

V něm je zřetelné, že prvně Gitlab Runner pomocí /bin/sh spustí skript, který testuje existenci různých Shell implementací v několika lokacích. A pokud je nalezena, je následně spuštěna. A tento Shell je pak využit i ke spouštění všech skriptů v .gitlab-ci.yml souboru. V případě zmíněného Docker FTP Deployer image tedy využije /bin/bash protože je nainstalován a je v podmínce dříve. Pokud by ale přidán nebyl, použil by /bin/sh.

const BashDetectShellScript = `if [ -x /usr/local/bin/bash ]; then
	exec /usr/local/bin/bash $@
elif [ -x /usr/bin/bash ]; then
	exec /usr/bin/bash $@
elif [ -x /bin/bash ]; then
	exec /bin/bash $@
elif [ -x /usr/local/bin/sh ]; then
	exec /usr/local/bin/sh $@
elif [ -x /usr/bin/sh ]; then
	exec /usr/bin/sh $@
elif [ -x /bin/sh ]; then
	exec /bin/sh $@
elif [ -x /busybox/sh ]; then
	exec /busybox/sh $@
else
	echo shell not found
	exit 1
fi

`
// ...

func (b *BashShell) GetConfiguration(info common.ShellScriptInfo) (*common.ShellConfiguration, error) { // ... if info.Type == common.LoginShell { script.CmdLine += " -l" script.Arguments = []string{"-l"} script.DockerCommand = []string{"sh", "-c", strings.ReplaceAll(BashDetectShellScript, "$@", "-l")} } else { script.DockerCommand = []string{"sh", "-c", strings.ReplaceAll(BashDetectShellScript, "$@", "")} }
// ...
}

Zkušenosti s Gitlab CI pipeline můžete sdílet s ostatními v komentářích

K tomuto článku již není možné přidávat další komentáře