Self-hosted Gitlab CI for PHP Symfony project

rteeom on 2023-06-18

Every few years in tech the bar of necessary minimum is raised. And that’s a good thing! But to keep up every project ought to be “in shape” and have a lot of tools set up: linters, tests, CI/CD pipeline, you name it…

In hope it could be of use to someone I’d like to share my humble efforts of configuring GitLab CI for php project.

Environment

I used self-hosted runner installed standalone on Digital Ocean droplet.

How To Install and Use Docker Compose on Ubuntu 22.04 | DigitalOcean Docker Compose is a tool that allows you to run multi-container application environments based on definitions set in a…www.digitalocean.com

How To Install and Use Docker on Ubuntu 22.04 | DigitalOcean Docker is an application that simplifies the process of managing application processes in containers. In this tutorial…www.digitalocean.com

The runner

Had no luck adding official gitlab repo (https://docs.gitlab.com/runner/install/linux-repository.html) to ubuntu/kinetic 22.10 lts so I went with downloading binaries (https://docs.gitlab.com/runner/install/linux-manually.html)

curl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_amd64.deb"
dpkg -i gitlab-runner_amd64.deb

Register runner.

Registering runners | GitLab Documentation for GitLab Community Edition, GitLab Enterprise Edition, Omnibus GitLab, and GitLab Runner.docs.gitlab.com

sudo gitlab-runner register

# you will be asked to provide url - https://gitlab.com
# registration token (CI/CD >> Settings >> Register runner)
# executor - docker
# name - whatever-you-like
# image - docker:stable
#/etc/gitlab-runner/config.toml
concurrent = 4
check_interval = 0
shutdown_timeout = 0

[session_server]
  session_timeout = 1800

[[runners]]
  name = "dedicated-runner"
  url = "https://gitlab.com"
  id = 24407945
  token = "glrt-your-gitlab-token"
  token_obtained_at = 2023-06-16T21:57:50Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "docker"
  environment = ["GIT_CONFIG_COUNT=1", "GIT_CONFIG_KEY_0=safe.directory", "GIT_CONFIG_VALUE_0=*"]
  [runners.cache]
    MaxUploadedArchiveSize = 0
  [runners.docker]
    tls_verify = false
    image = "docker:stable"
    privileged = true
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/cache", "/home/gitlab-runner/shared:/builds/shared"]
    shm_size = 0
  [runners.custom_build_dir]
    enabled = true
default:
  image: docker:24.0.2-git
  tags:
    - dedicated-runner-2

  artifacts:
    paths:
      - vendor/
      - var/cache/
    expire_in: 10 min

services:
  - name: docker:24.0.2-dind
    alias: dockerdaemon

variables:
  # Tell docker CLI how to talk to Docker daemon.
  DOCKER_HOST: tcp://dockerdaemon:2375/
  # Use the overlayfs driver for improved performance.
  DOCKER_DRIVER: overlay2
  # Disable TLS since we're running inside local network.
  DOCKER_TLS_CERTDIR: ""
  GIT_CLONE_PATH: $CI_BUILDS_DIR/shared/$CI_COMMIT_SHORT_SHA/$CI_JOB_ID

stages:
  - build
  - lint
  - test

build:
  stage: build
  before_script:
    - docker login -u swiftcode -p "$DOCKER_HUB_TOKEN"
  script:
    - docker compose build
    - docker compose run --rm php sh -c "composer install && pwd && ls -la"

fixer:
  stage: lint
  needs:
    - build
  script:
    - docker login -u swiftcode -p "$DOCKER_HUB_TOKEN"
    - docker compose run --rm php ./vendor/bin/php-cs-fixer fix -v --dry-run --using-cache=no

psalm:
  needs:
    - build
  stage: lint
  script:
    - ls -la
    - pwd
    - touch psalm.txt
    - docker login -u swiftcode -p "$DOCKER_HUB_TOKEN"
    - docker compose run --rm php ./vendor/bin/psalm --no-cache --no-progress --threads=1

tests:
  needs:
    - build
  stage: test
  before_script:
    - docker login -u swiftcode -p "$DOCKER_HUB_TOKEN"
    - docker-compose up -d --remove-orphans
  script:
    - docker-compose exec php sh -c "./vendor/bin/phpunit"
  after_script:
    - docker-compose down --rmi local --volumes

Downsides:

Docker in docker is good for isolation, too good… so every job is run inside own docker namespace and is a fresh instance, so caching is a challenge. But that is another story…