The shell used to execute script lines depends on the os, and can be configured. For Linux environments, the default shell is bash.
But when it comes to expanding environment variables, things are a bit more complicated. Before the shell session which runs your scripts can be created, GitLab needs to be able to parse the ci file to evaluate triggers, determine the build environment, etc. Because of this, GitLab parses environment variables iteratively, with the rules in each round a bit different than a normal shell session, and a bit different from each other. From the docs:
There are three expansion mechanisms:
- GitLab
- GitLab Runner
- Execution shell environment
In the GitLab stage,
The expanded part needs to be in a form of $variable, or ${variable} or %variable%. Each form is handled in the same way, no matter which OS/shell handles the job, because the expansion is done in GitLab before any runner gets the job.
GitLab Runner then takes another crack at expanding the additional set of variables available at runtime, such as e.g. CI_BUILDS_DIR.
Again from the docs:
GitLab Runner internal variable expansion mechanism
- Supported: project/group variables, .gitlab-ci.yml variables, config.toml variables, and variables from triggers, pipeline schedules, and manual pipelines.
- Not supported: variables defined inside of scripts (for example, export MY_VARIABLE="test").
The runner uses Go’s os.Expand() method for variable expansion. It means that it handles only variables defined as $variable and ${variable}. What’s also important, is that the expansion is done only once, so nested variables may or may not work, depending on the ordering of variables definitions, and whether nested variable expansion is enabled in GitLab.
Finally, the shell executed script lines with the full context.
See the GitLab-ci runner docs and the GitLab runner environment variables docs for more information and configuration options.