Operations 11 min read

Streamline Go Releases with GitLab CI, Makefile, and Jenkins

This guide walks through the challenges of manual Go releases, compares two naive deployment approaches, and presents a streamlined solution using GitLab CI pipelines, Makefile automation, and Jenkins to decouple development from operations while ensuring reliable versioned deployments.

Go Development Architecture Practice
Go Development Architecture Practice
Go Development Architecture Practice
Streamline Go Releases with GitLab CI, Makefile, and Jenkins

Problem with Manual Go Releases

Traditional Go release processes often involve ad‑hoc steps such as manually switching environment variables, compiling binaries, and handing files to operations for deployment, which leads to error‑prone, lengthy workflows.

Naïve Approaches

Approach 1

Developers modify local environment files to production settings.

Compile the binary.

Send the binary to operations.

Operations replace the live file and restart the process.

Approach 2

Developers commit code to GitLab.

Operations pull the repository.

Operations compile the binary.

Operations replace the live file and restart.

Proposed CI/CD Solution

Developers push code to a GitLab repository.

Tag the commit to trigger a build (using .gitlab-ci.yml and a Makefile).

The built artifact is packaged with a version tag (e.g., 1.0.0) and uploaded to a version server.

Jenkins (or another deployment tool) picks the tagged version and deploys it to the target web server.

GitLab CI Overview

GitLab CI is integrated into GitLab from version 8.0. Adding a .gitlab-ci.yml file and configuring a Runner enables pipelines to run automatically on each tag push.

Key Concepts

Pipeline

A pipeline represents a complete build job and can contain multiple stages such as dependency installation, testing, compilation, and deployment.

+------------------+   trigger   +----------------+
|   Commit / MR    +----------->|    Pipeline    |
+------------------+            +----------------+

Stages

Stages define ordered phases of a pipeline. All stages run sequentially; a failure in any stage aborts the pipeline.

+--------------------------------------------------------+
|                     Pipeline                           |
|  +-----------+   +------------+   +------------+      |
|  |  Stage 1  |-> |  Stage 2   |-> |  Stage 3   |      |
|  +-----------+   +------------+   +------------+      |
+--------------------------------------------------------+

Jobs

Jobs are the actual tasks executed within a stage. Jobs in the same stage run in parallel, and all must succeed for the stage to pass.

+------------------------------------------+
|                Stage 1                  |
|  +---------+  +---------+  +---------+   |
|  |  Job 1  |  |  Job 2  |  |  Job 3  |   |
|  +---------+  +---------+  +---------+   |
+------------------------------------------+

Makefile Basics

A Makefile defines reusable build commands, allowing a single make invocation to compile and package a Go project. go build . Typical usage in larger projects:

make build
# or
make install

Example Project Structure

main.go

Makefile

Sample Makefile:

BINARY_NAME=hello
build:
    go build -o $(BINARY_NAME) -v
    ./$(BINARY_NAME)

Explanation:

Line 1 defines a variable BINARY_NAME with value hello.

Line 2 declares the build target.

Line 3 compiles the binary using the variable.

Line 4 runs the resulting binary.

Deployment Process

GitLab Runner Installation

# For Debian/Ubuntu
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash
sudo apt-get install gitlab-ci-multi-runner

# For CentOS
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.rpm.sh | sudo bash
sudo yum install gitlab-ci-multi-runner

Registering a Runner

Open the project settings in GitLab and navigate to Runners.

Run sudo gitlab-ci-multi-runner register.

Provide the CI URL, registration token, a name, and select the shell executor.

Verify registration with sudo gitlab-runner list.

.gitlab-ci.yml Example

before_script:
  - export GOPATH=$GOPATH:/usr/local/${CI_PROJECT_NAME}
  - cd /usr/local/${CI_PROJECT_NAME}/src/${CI_PROJECT_NAME}
  - export VERSION=`echo ${CI_COMMIT_TAG} | awk -F"_" '{print $1}'`

stages:
  - build
  - deploy

build-tags:
  stage: build
  script:
    - make ENV="prod" VERSION=${VERSION}
    - if [ ! -d "/data/code/project_name/tags/${VERSION}" ]; then mkdir -p "/data/code/project_name/tags/${VERSION}/"; fi
    - cd compiler/
    - mv -f project_name.tar.gz "/data/code/project_name/tags/${VERSION}/"
  only:
    - tags

deploy-tags:
  stage: deploy
  script:
    - cd /data/code/project_name/tags/
    - svn add ${VERSION}
    - svn commit -m "add ${CI_COMMIT_TAG}"
  only:
    - tags

Project Makefile

export VERSION=1.0.0
export ENV=prod
export PROJECT=project_name

TOPDIR=$(shell pwd)
OBJ_DIR=$(OUTPUT)/$(PROJECT)
SOURCE_BINARY_DIR=$(TOPDIR)/bin
SOURCE_BINARY_FILE=$(SOURCE_BINARY_DIR)/$(PROJECT)
SOURCE_MAIN_FILE=main.go

BUILD_TIME=`date +%Y%m%d%H%M%S`
BUILD_FLAG=-ldflags "-X main.version=$(VERSION) -X main.buildTime=$(BUILD_TIME)"

OBJTAR=$(OBJ_DIR).tar.gz

all: build pack
	@echo "
ALL DONE"
	@echo "Program:       " $(PROJECT)
	@echo "Version:       " $(VERSION)
	@echo "Env:           " $(ENV)

build:
	@echo "start go build...."
	@rm -rf $(SOURCE_BINARY_DIR)/*
	@go build $(BUILD_FLAG) -o $(SOURCE_BINARY_FILE) $(SOURCE_MAIN_FILE)

pack:
	@echo "
packing...."
	@tar czvf $(OBJTAR) -C $(OBJ_DIR) .

Running the Deployment

Typical workflow after implementing the CI pipeline:

git commit -a -m "prepare tag test"
git push
# Create a tag
git tag -a "1.0.0" -m "1.0.0"
git push origin 1.0.0

The tag push triggers the GitLab CI pipeline, which builds the binary, packages it, and hands it off to Jenkins (or any deployment tool) for final deployment.

Conclusion

Release processes vary by team size and project complexity. Small teams may still use manual releases, but larger teams benefit from a standardized CI/CD workflow that combines GitLab CI, Makefile automation, and a deployment system to ensure reliable, repeatable releases.

CI/CDdeploymentGoGitLab CIJenkins
Go Development Architecture Practice
Written by

Go Development Architecture Practice

Daily sharing of Golang-related technical articles, practical resources, language news, tutorials, real-world projects, and more. Looking forward to growing together. Let's go!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.