• Reading time ~ 5 min
  • 25.03.2024

Let's take a look at compiling a project in GO and how it works in Gitlab CI.

Compilation

To build, we need to call go build -o binary_name.

If your project has imports from third-party repositories, then there are external dependencies. Compiling for GO will require all source code, including dependency library code. Therefore, you need to make sure that third-party libraries that may not be stored in your repository are also available for compilation.

Vendor or download

At the moment, the current version is 1.14.2. Starting from version GO 1.11, module functionality (go modules) is available.

GO modules work in such a way that GO itself loads all third-party libraries when go runcalled , go build or when all libraries are explicitly loaded - . go get ./...

The source code of downloaded libraries is saved to $GOPATH/pkg/mod or to $HOME/go/pkg/modif an environment $GOPATH variable is not set.

There is an approach to dealing with dependencies called vendoring. GO modules support vendoring so that if you specify -mod=vendor_dirthe , the dependencies will be downloaded to the vendor_dirfolder. This folder can be inside your repository and pushed into the Git repository along with your source code.

When using vendoring, dependencies will be downloaded once and shipped with the source code of the project, which will allow for faster builds.

Gitlab

Gitlab provides the ability to perform various tasks after pushing to the repository.

To use this functionality, you need to add .gitlab-ci.ymlthe .

Compilation in Gitlab

You need to define the stages and the specific actions to be performed in those steps. For each action, you can specify a docker image.

Your project code will be automatically downloaded to the /builds/{project_group}/{project_name}folder. This means that there is no need to download it manually.

In the code below, I've defined the build step and the eponymous action in it:

stages:
  — build
build:
  image: rhaps1071/golang-1.14-alpine-git
  stage: build
  script:
    — go get ./...
    — GOARCH=amd64 GOOS=linux go build -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/binary
  artifacts:
    paths:
      — binary

The docker image of my authorship rhaps1071/golang-1.14-alpine-git is used here.
It adds a command git to the golang:1.14-alpine. This modification is necessary for the command go get ./...that loads dependencies and uses git clonethe .
The base image is based on Alpine Linux, as this distribution weighs only a few megabytes.
However, the size golang:1.14-alpine weighs in at 370MB, which is a lot, but still a two-fold gain compared to golang:1.14 the (809MB) based on Ubuntu.

I don't use vendoring, so I need to download dependencies to build. To do this, call the commandgo get ./....

As I said above, the code in Gitlab CI is located in the , which is outside $GOPATHthe /builds/{project_name}/{project_folder}.
In order for this to work, there must be a . go.modYou can create it by using go mod initthe . If you don't have a file go.mod , the team go get ./... won't be able to figure out the dependencies.

If your project doesn't use GO modules, you'll need to move your source code to the $GOPATH.
In Gitlab CI, this copy will look like this:
- cp /builds/* $GOPATH/src/

The artifacts statement allows you to save any of the .gitlab-ci.yml files or folders for download, as well as for use in the next stages of Gitlab CI.

SSH

Deployment Let's take a look at the deployment of the resulting binary using SSH.

deploy_stage:
  image: kroniak/ssh-client
  stage: deploy
  environment:
    name: stage
    url: http://stage.project.com
  when: manual
  script:
    — mkdir -p ~/.ssh
    — echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    — chmod -R 700 ~/.ssh
    — echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
    — chmod 644 ~/.ssh/known_hosts
    — echo "$CONFIG" > ./config.json
    — scp -P$SSH_PORT -r ./config.json $SSH_USER@$SSH_HOST:/var/www/project/config/
    — scp -P$SSH_PORT -r ./binary $SSH_USER@$SSH_HOST:~/binary_tmp
    — ssh -p$SSH_PORT $SSH_USER@$SSH_HOST 'sudo service project stop && cp ~/binary_tmp /var/www/project/binary && sudo service project restart'

Here again, a customized Alpine Linux image is used — 12.1MB kroniak/ssh-client in size. This time, it additionally pre-installed with the ssh client, thanks to which ssh scp

the commands and .The deployment logic is as follows:

  • The following Gitlab variables are enabled: $SSH_PRIVATE_KEY - private key to access the server;
    $SSH_USER, $SSH_HOST, $SSH_PORT is the login and address of the server for deployment;
    $SSH_KNOWN_HOSTS - An entry for the .ssh/known_hosts file through which the server is validated.
    $CONFIG – the contents of our service's configuration file in json format;
  • When deploying, first fill in all the necessary files with data from Gitlab variables;
  • All the necessary files are copied to the server using scp. Could be used rsyncas it only copies files that have changed and copies them in archived form. However, when it comes to 1-2 files, there is almost no winning.
  • The last step is to replace the binary and restart our service;

The resulting file .gitlab-ci.yml looks like this in its entirety:

stages:
  — build
  — deploy
build:
  image: rhaps1071/golang-1.14-alpine-git
  stage: build
  script:
    — go get ./...
    — GOARCH=amd64 GOOS=linux go build -ldflags "-extldflags '-static'" -o $CI_PROJECT_DIR/binary
  artifacts:
    paths:
      — binary

deploy_stage:
  image: kroniak/ssh-client
  stage: deploy
  environment:
    name: stage
    url: http://stage.project.com
  when: manual
  script:
    — mkdir -p ~/.ssh
    — echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    — chmod -R 700 ~/.ssh
    — echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
    — chmod 644 ~/.ssh/known_hosts
    — echo "$CONFIG" > ./config.json
    — scp -P$SSH_PORT -r ./config.json $SSH_USER@$SSH_HOST:/var/www/project/config/
    — scp -P$SSH_PORT -r ./binary $SSH_USER@$SSH_HOST:~/binary_tmp
    — ssh -p$SSH_PORT $SSH_USER@$SSH_HOST 'sudo service project stop && cp ~/binary_tmp /var/www/project/binary && sudo service project restart'

Other articles

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

ABOUT

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

About author CrazyBoy49z
WORK EXPERIENCE
Contact
Ukraine, Lutsk
+380979856297