If a GitLab project is configured on GitLab CI, is there a way to run the build locally?
I don't want to turn my laptop into a build "runner", I just want to take advantage of Docker and .gitlab-ci.yml to run tests locally (i.e. it's all pre-configured). Another advantage of that is that I'm sure that I'm using the same environment locally and on CI.
Here is an example of how to run Travis builds locally using Docker, I'm looking for something similar with GitLab.
Since a few months ago this is possible using gitlab-runner:
gitlab-runner exec docker my-job-name
Note that you need both docker and gitlab-runner installed on your computer to get this working.
You also need the image key defined in your .gitlab-ci.yml file. Otherwise won't work.
Here's the line I currently use for testing locally using gitlab-runner:
gitlab-runner exec docker test --docker-volumes "/home/elboletaire/.ssh/id_rsa:/root/.ssh/id_rsa:ro"
Note: You can avoid adding a --docker-volumes with your key setting it by default in /etc/gitlab-runner/config.toml. See the official documentation for more details. Also, use gitlab-runner exec docker --help to see all docker-based runner options (like variables, volumes, networks, etc.).
Due to the confusion in the comments, I paste here the gitlab-runner --help result, so you can see that gitlab-runner can make builds locally:
gitlab-runner --help
NAME:
gitlab-runner - a GitLab Runner
USAGE:
gitlab-runner [global options] command [command options] [arguments...]
VERSION:
1.1.0~beta.135.g24365ee (24365ee)
AUTHOR(S):
Kamil TrzciĆski <ayufan#ayufan.eu>
COMMANDS:
exec execute a build locally
[...]
GLOBAL OPTIONS:
--debug debug mode [$DEBUG]
[...]
As you can see, the exec command is to execute a build locally.
Even though there was an issue to deprecate the current gitlab-runner exec behavior, it ended up being reconsidered and a new version with greater features will replace the current exec functionality.
Note that this process is to use your own machine to run the tests using docker containers. This is not to define custom runners. To do so, just go to your repo's CI/CD settings and read the documentation there. If you wanna ensure your runner is executed instead of one from gitlab.com, add a custom and unique tag to your runner, ensure it only runs tagged jobs and tag all the jobs you want your runner to be responsible of.
I use this docker-based approach:
Edit: 2022-10
docker run --entrypoint bash --rm -w $PWD -v $PWD:$PWD -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest -c 'git config --global --add safe.directory "*";gitlab-runner exec docker test'
For all git versions > 2.35.2. You must add safe.directory within the container to avoid fatal: detected dubious ownership in repository at.... This also true for patched git versions < 2.35.2. The old command will not work anymore.
Details
0. Create a git repo to test this answer
mkdir my-git-project
cd my-git-project
git init
git commit --allow-empty -m"Initialize repo to showcase gitlab-runner locally."
1. Go to your git directory
cd my-git-project
2. Create a .gitlab-ci.yml
Example .gitlab-ci.yml
image: alpine
test:
script:
- echo "Hello Gitlab-Runner"
3. Create a docker container with your project dir mounted
docker run -d \
--name gitlab-runner \
--restart always \
-v $PWD:$PWD \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
(-d) run container in background and print container ID
(--restart always) or not?
(-v $PWD:$PWD) Mount current directory into the current directory of the container - Note: On Windows you could bind your dir to a fixed location, e.g. -v ${PWD}:/opt/myapp. Also $PWD will only work at powershell not at cmd
(-v /var/run/docker.sock:/var/run/docker.sock) This gives the container access to the docker socket of the host so it can start "sibling containers" (e.g. Alpine).
(gitlab/gitlab-runner:latest) Just the latest available image from dockerhub.
4. Execute with
Avoid fatal: detected dubious ownership in repository at... More info
docker exec -it -w $PWD gitlab-runner git config --global --add safe.directory "*"
Actual execution
docker exec -it -w $PWD gitlab-runner gitlab-runner exec docker test
# ^ ^ ^ ^ ^ ^
# | | | | | |
# (a) (b) (c) (d) (e) (f)
(a) Working dir within the container. Note: On Windows you could use a fixed location, e.g. /opt/myapp.
(b) Name of the docker container
(c) Execute the command "gitlab-runner" within the docker container
(d)(e)(f) run gitlab-runner with "docker executer" and run a job named "test"
5. Prints
...
Executing "step_script" stage of the job script
$ echo "Hello Gitlab-Runner"
Hello Gitlab-Runner
Job succeeded
...
Note: The runner will only work on the commited state of your code base. Uncommited changes will be ignored. Exception: The .gitlab-ci.yml itself does not have be commited to be taken into account.
Note: There are some limitations running locally. Have a look at limitations of gitlab runner locally.
I'm currently working on making a gitlab runner that works locally.
Still in the early phases, but eventually it will become very relevant.
It doesn't seem like gitlab want/have time to make this, so here you go.
https://github.com/firecow/gitlab-runner-local
If you are running Gitlab using the docker image there: https://hub.docker.com/r/gitlab/gitlab-ce, it's possible to run pipelines by exposing the local docker.sock with a volume option: -v /var/run/docker.sock:/var/run/docker.sock. Adding this option to the Gitlab container will allow your workers to access to the docker instance on the host.
The GitLab runner appears to not work on Windows yet and there is an open issue to resolve this.
So, in the meantime I am moving my script code out to a bash script, which I can easily map to a docker container running locally and execute.
In this case I want to build a docker container in my job, so I create a script 'build':
#!/bin/bash
docker build --pull -t myimage:myversion .
in my .gitlab-ci.yaml I execute the script:
image: docker:latest
services:
- docker:dind
before_script:
- apk add bash
build:
stage: build
script:
- chmod 755 build
- build
To run the script locally using powershell I can start the required image and map the volume with the source files:
$containerId = docker run --privileged -d -v ${PWD}:/src docker:dind
install bash if not present:
docker exec $containerId apk add bash
Set permissions on the bash script:
docker exec -it $containerId chmod 755 /src/build
Execute the script:
docker exec -it --workdir /src $containerId bash -c 'build'
Then stop the container:
docker stop $containerId
And finally clean up the container:
docker container rm $containerId
Another approach is to have a local build tool that is installed on your pc and your server at the same time.
So basically, your .gitlab-ci.yml will basically call your preferred build tool.
Here an example .gitlab-ci.yml that i use with nuke.build:
stages:
- build
- test
- pack
variables:
TERM: "xterm" # Use Unix ASCII color codes on Nuke
before_script:
- CHCP 65001 # Set correct code page to avoid charset issues
.job_template: &job_definition
except:
- tags
build:
<<: *job_definition
stage: build
script:
- "./build.ps1"
test:
<<: *job_definition
stage: test
script:
- "./build.ps1 test"
variables:
GIT_CHECKOUT: "false"
pack:
<<: *job_definition
stage: pack
script:
- "./build.ps1 pack"
variables:
GIT_CHECKOUT: "false"
only:
- master
artifacts:
paths:
- output/
And in nuke.build i've defined 3 targets named like the 3 stages (build, test, pack)
In this way you have a reproducible setup (all other things are configured with your build tool) and you can test directly the different targets of your build tool.
(i can call .\build.ps1 , .\build.ps1 test and .\build.ps1 pack when i want)
I am on Windows using VSCode with WSL
I didn't want to register my work PC as a runner so instead I'm running my yaml stages locally to test them out before I upload them
$ sudo apt-get install gitlab-runner
$ gitlab-runner exec shell build
yaml
image: node:10.19.0 # https://hub.docker.com/_/node/
# image: node:latest
cache:
# untracked: true
key: project-name
# key: ${CI_COMMIT_REF_SLUG} # per branch
# key:
# files:
# - package-lock.json # only update cache when this file changes (not working) #jkr
paths:
- .npm/
- node_modules
- build
stages:
- prepare # prepares builds, makes build needed for testing
- test # uses test:build specifically #jkr
- build
- deploy
# before_install:
before_script:
- npm ci --cache .npm --prefer-offline
prepare:
stage: prepare
needs: []
script:
- npm install
test:
stage: test
needs: [prepare]
except:
- schedules
tags:
- linux
script:
- npm run build:dev
- npm run test:cicd-deps
- npm run test:cicd # runs puppeteer tests #jkr
artifacts:
reports:
junit: junit.xml
paths:
- coverage/
build-staging:
stage: build
needs: [prepare]
only:
- schedules
before_script:
- apt-get update && apt-get install -y zip
script:
- npm run build:stage
- zip -r build.zip build
# cache:
# paths:
# - build
# <<: *global_cache
# policy: push
artifacts:
paths:
- build.zip
deploy-dev:
stage: deploy
needs: [build-staging]
tags: [linux]
only:
- schedules
# # - branches#gitlab-org/gitlab
before_script:
- apt-get update && apt-get install -y lftp
script:
# temporarily using 'verify-certificate no'
# for more on verify-certificate #jkr: https://www.versatilewebsolutions.com/blog/2014/04/lftp-ftps-and-certificate-verification.html
# variables do not work with 'single quotes' unless they are "'surrounded by doubles'"
- lftp -e "set ssl:verify-certificate no; open mediajackagency.com; user $LFTP_USERNAME $LFTP_PASSWORD; mirror --reverse --verbose build/ /var/www/domains/dev/clients/client/project/build/; bye"
# environment:
# name: staging
# url: http://dev.mediajackagency.com/clients/client/build
# # url: https://stg2.client.co
when: manual
allow_failure: true
build-production:
stage: build
needs: [prepare]
only:
- schedules
before_script:
- apt-get update && apt-get install -y zip
script:
- npm run build
- zip -r build.zip build
# cache:
# paths:
# - build
# <<: *global_cache
# policy: push
artifacts:
paths:
- build.zip
deploy-client:
stage: deploy
needs: [build-production]
tags: [linux]
only:
- schedules
# - master
before_script:
- apt-get update && apt-get install -y lftp
script:
- sh deploy-prod
environment:
name: production
url: http://www.client.co
when: manual
allow_failure: true
The idea is to keep check commands outside of .gitlab-ci.yml. I use Makefile to run something like make check and my .gitlab-ci.yml runs the same make commands that I use locally to check various things before committing.
This way you'll have one place with all/most of your commands (Makefile) and .gitlab-ci.yml will have only CI-related stuff.
I have written a tool to run all GitLab-CI job locally without have to commit or push, simply with the command ci-toolbox my_job_name.
The URL of the project : https://gitlab.com/mbedsys/citbx4gitlab
Years ago I build this simple solution with Makefile and docker-compose to run the gitlab runner in docker, you can use it to execute jobs locally as well and should work on all systems where docker works:
https://gitlab.com/1oglop1/gitlab-runner-docker
There are few things to change in the docker-compose.override.yaml
version: "3"
services:
runner:
working_dir: <your project dir>
environment:
- REGISTRATION_TOKEN=<token if you want to register>
volumes:
- "<your project dir>:<your project dir>"
Then inside your project you can execute it the same way as mentioned in other answers:
docker exec -it -w $PWD runner gitlab-runner exec <commands>..
I recommend using gitlab-ci-local
https://github.com/firecow/gitlab-ci-local
It's able to run specific jobs as well.
It's a very cool project and I have used it to run simple pipelines on my laptop.
I am having trouble accessing my gcloud compute engine via Travis-CI so I can have CI/CD capabilities.
So far using my current code I am able to use my git repository to start up docker containers on Travis CI to see that they work.
I am then able to get them to build, tag, and deploy to the google cloud container registry with no issues.
However, when I get to the step where I want to ssh into my compute instance to pull and run my containers I run into issues.
I have tried using the gcloud compute ssh --command but I run into issues with gcloud not being installed on my instance. Error received:
If I try running a gcloud command it just says gcloud is missing.
bash: gcloud: command not found
The command "gcloud compute ssh --quiet --project charged-formula-262616 --zone us-west1-b instance-1 --command="gcloud auth configure-docker "" failed and exited with 127 during.
I have also tried downloading the gcloud sdk and running the docker config again but I start receiving the Error bellow.
bash
Error response from daemon: unauthorized: You don't have the needed permissions to perform this operation, and you may have invalid credentials. To authenticate your request, follow the steps in: https://cloud.google.com/container-registry/docs/advanced-authentication
Using default tag: latest
I am able to ssh into it using putty as another user to pull from the repository with no issues and start the containers and have the gcloud command exists.
The only thing I could think of is the two accounts used for ssh are different but both keys are added to the instance and I don't see where I can control their permissions. I also created a service account for travis ci and granted it all the same permissions as the compute service account and still no dice...
Any help or advice would be much appreciated!
My travis file looks like this
sudo: required
language: generic
services:
- docker
env:
global:
- SHA=$(git rev-parse HEAD)
- CLOUDSDK_CORE_DISABLE_PROMPTS=1
cache:
directories:
- "$HOME/google-cloud-sdk/"
before_install:
- openssl aes-256-cbc -K $encrypted_0c35eebf403c_key -iv $encrypted_0c35eebf403c_iv
-in secrets.tar.enc -out secrets.tar -d
- tar xvf secrets.tar
- if [ ! -d "$HOME/google-cloud-sdk/bin" ]; then rm -rf $HOME/google-cloud-sdk; export
CLOUDSDK_CORE_DISABLE_PROMPTS=1; curl https://sdk.cloud.google.com | bash; fi
- source $HOME/google-cloud-sdk/path.bash.inc
- gcloud auth activate-service-account --key-file service-account.json
- gcloud components update
- gcloud components install docker-credential-gcr
- gcloud version
- eval $(ssh-agent -s)
- chmod 600 deploy_key_open
- echo -e "Host $SERVER_IP_ADDRESS\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- ssh-add deploy_key_open
- gcloud auth configure-docker
# - sudo docker pull gcr.io/charged-formula-262616/web-client
# - sudo docker pull gcr.io/charged-formula-262616/web-nginx
deploy:
provider: script
script: bash ./deploy.sh
on:
branch: master
and the bash script is
# docker build -t gcr.io/charged-formula-262616/web-client:latest -t gcr.io/charged-formula-262616/web-client:$SHA -f ./client/Dockerfile ./client
# docker build -t gcr.io/charged-formula-262616/web-nginx:latest -t gcr.io/charged-formula-262616/web-nginx:$SHA -f ./nginx/Dockerfile ./nginx
# docker build -t gcr.io/charged-formula-262616/web-server:latest -t gcr.io/charged-formula-262616/web-server:$SHA -f ./server/Dockerfile ./server
docker push gcr.io/charged-formula-262616/web-client
docker push gcr.io/charged-formula-262616/web-nginx
docker push gcr.io/charged-formula-262616/web-server
# curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-274.0.1-linux-x86_64.tar.gz
# tar zxvf google-cloud-sdk-274.0.1-linux-x86_64.tar.gz google-cloud-sdk
# ./google-cloud-sdk/install.sh
# sudo docker container stop $(docker container ls -aq)
# echo "1 " | gcloud init
ssh -o StrictHostKeyChecking=no -i deploy_key_open travis-ci#104.196.226.118 << EOF
source /home/travis-ci/google-cloud-sdk/path.bash.inc
gcloud auth configure-docker
sudo docker-credential-gcloud list
sudo docker pull gcr.io/charged-formula-262616/web-nginx
sudo docker pull gcr.io/charged-formula-262616/web-client
sudo docker pull gcr.io/charged-formula-262616/web-server
sudo docker run --rm -d -p 3000:3000 gcr.io/charged-formula-262616/web-client
sudo docker run --rm -d -p 80:80 -p 443:443 gcr.io/charged-formula-262616/web-nginx
sudo docker run --rm -d -p 5000:5000 gcr.io/charged-formula-262616/web-server
sudo docker run --rm -d -v /database_data:/var/lib/postgresql/data -e POSTGRES_USER -e POSTGRES_PASSWORD -e POSTGRES_DB postgres
EOF
The error you posted includes a link to Authentication methods where suggests some mechanisms to authenticate docker such as:
gcloud auth configure-docker
And other more advanced authentication methods. I recommend that you check this out as it will guide you to solve your issue.
To install the gcloud command you can follow the guide in Installing Google Cloud SDK. That for Linux are this.
I deleted my previous question because it was not very clear, and the problem was not clearly exposed. I have an instance #aws, a repository #gitlab, and gitlab CI is setup.
I made a little app in node.js because I want to try all these new stuff.
But, when gitlab-ci runs the script, pm2 creates a "source" directory in my folder, then copied all my files in this directory, which is appearently the Current Working Directory (CWD).
That's a surprising behavior, and I'm not comfortable with it.
Anyone knows why ? Is it normal ? Why can't my files stay in ~/projet2/, as I set up ?
When I run pm2 show projet2, I can see the exec cwd is /home/ubuntu/projet2/source while source is a folder I've never created !
.git-ci.yml
# This file is a template, and might need editing before it works on your project.
# Official framework image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/node/tags/
image: node:alpine
stages:
- deploy
deploy:
stage: deploy
before_script:
# Install ssh-agent if not already installed, it is required by Docker.
# (change apt-get to yum if you use a CentOS-based image)
- 'which ssh-agent || ( apk add --update openssh )'
# Add bash
- apk add --update bash
# Add git
- apk add --update git
# Run ssh-agent (inside the build environment)
- eval $(ssh-agent -s)
# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
- echo "$SSH_PRIVATE_KEY" > "./pk.pem"
- chmod 400 ./pk.pem
- echo "$SSH_PRIVATE_KEY" | ssh-add -
# For Docker builds disable host key checking. Be aware that by adding that
# you are suspectible to man-in-the-middle attacks.
# WARNING: Use this only with the Docker executor, if you use it with shell
# you will overwrite your user's SSH config.
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
# In order to properly check the server's host key, assuming you created the
# SSH_SERVER_HOSTKEYS variable previously, uncomment the following two lines
# instead.
# - mkdir -p ~/.ssh
# - '[[ -f /.dockerenv ]] && echo "$SSH_SERVER_HOSTKEYS" > ~/.ssh/known_hosts'
script:
- npm i -g pm2
- pm2 deploy ecosystem.config.js production setup
- pm2 deploy ecosystem.config.js production
only:
- master
ecosystem.config.js
module.exports = {
apps: [{
name: 'projet2',
script: '/home/ubuntu/projet2/index.js',
cwd: '/home/ubuntu/projet2/'
}],
deploy: {
production: {
user: 'ubuntu',
host: 'xxxxxxxxxxxx',
ref: 'origin/master',
repo: 'git#gitlab.com:xxxxxxx/projet2.git',
key: './pk.pem',
path: '/home/ubuntu/projet2/',
'post-deploy': 'npm install && pm2 startOrRestart /home/ubuntu/projet2/ecosystem.config.js'
}
}
}
The answer is: Yes! This is normal behavior!
It is to be expected, since you are running things with pm2 now, and pm2 knows how to handle it.
By running:
pm2 deploy ecosystem.config.js someName
the pm2 is making an SSH to the provided host, using the provided user and key. Then, on a successful connection to the provided host, pm2 proceeds to try and do a git pull from the provided referenced branch inside ref, which belongs to the provided repo. The pulled data will be placed in the provided path inside 'path', with the addition of a 'source' directory. After a successful pull, the post-deploy will be triggered, which is in charge of doing the npm install and then some more stuff (depending on what you tell it to do). But nevertheless, the creation of the source folder is something that is built-in to the pm2 mechanism, and is to be expected. It shouldn't bother you too much.
I am using gitlab-ci to publish react native app using expo cli but the pipeline gets stuck at the tunnel connected stage. How do I fix this issue ?
cache:
paths:
- node_modules/
stages:
- deploy
before_script:
- npm ci
expo-deployments:
stage: deploy
script:
- echo "EXPO_USERNAME=""$EXPO_USERNAME" >> .env
- echo "EXPO_PASSWORD=""$EXPO_PASSWORD" >> .env
- npx expo login -u "$EXPO_USERNAME" -p "$EXPO_PASSWORD"
- cat /proc/sys/fs/inotify/max_user_watches
- echo fs.inotify.max_user_watches=524288 | tee -a /etc/sysctl.conf && sysctl -p
- npx expo publish --non-interactive
Here is the pipeline screenshot.
Not really sure what might be the issue. Tried to search but didn't find any concrete solution specific to this problem.
I want to auto deploy node.js project on gitlab.
Currently I'm using below configuration on .gitlab-ci.yml
deploy_to_dev_aws:
only:
- development
script:
- echo "$EC2_SSH_KEY" >> "key.pem"
- chmod 600 key.pem
- ssh -T -i key.pem -o StrictHostKeyChecking=no ubuntu#$EC2_HOST_IP <<EOF
- cd ~/projects
- rm myproject
- git checkout git://myprojectpath
- cd myproject
- pm2 delete all
- pm2 start app.js
- logout
- EOF
stage: build
Is this right way, as I'm log in into ec2 and performing all operations?
What are other ways to do the same?
I found a way to deploy using ssm agent by which we can deploy to multiple EC2 instances using tags(.pem key not required)
Steps:
1) Install SSM on EC2 instance, tag that instance as environment=qa
2) Use Gitlab runner to send command to this tagged instances
deploy_to_prod_dev_aws:
image: python:latest
only:
- qa
script:
- pip install awscli
- export AWS_ACCESS_KEY_ID=$AWS_KEY_ID
- export AWS_SECRET_ACCESS_KEY=$AWS_SECRET
- export AWS_DEFAULT_REGION=$AWS_REGION
- aws ssm send-command --targets "Key=tag:environment,Values=qa" --document-name "AWS-RunShellScript" --comment "Deployment" --parameters commands="cd /project && git clean -fd && git fetch && git checkout qa && git pull origin qa && npm install && pm2 delete all && pm2 start app.js" --output text
stage: build
environment:
name: qa
In above command
--targets specifies which ec2 instances to which we are be deploying defined by tags
--parameters commands defines which commands to run on ec2 instance.I ran git pull with latest code & pm2 start
Hope this will help someone.