.. SPDX-FileCopyrightText: 2022 cusy GmbH
..
.. SPDX-License-Identifier: BSD-3-Clause
Migrating GitHub Actions
========================
GitLab CI/CD and GitHub Actions have some similarities in configuration, making
migration to GitLab CI/CD relatively easy:
* Workflow configuration files are written in
:doc:`/data-processing/serialisation-formats/yaml/index` and are stored in the
repository along with the code.
* Workflows contain one or more jobs.
* Jobs include one or more steps or individual commands.
* Jobs can run on either managed or self-hosted machines.
However, there are also some differences, and this guide will show you the main
differences so that you can migrate your workflow to GitLab CI/CD.
Jobs
----
Jobs in GitHub Actions are very similar to jobs in GitLab CI/CD. Both have the
following characteristics:
* Jobs contain a series of steps or scripts that are executed in sequence.
* Jobs can be run on separate machines or in separate containers.
* Jobs are executed in parallel by default, but can also be configured to run
sequentially.
* Jobs can execute a script or shell command, and in GitHub Actions all scripts
are specified with the ``run`` key. In GitLab CI/CD, however, the script steps
are specified with the ``script`` key.
Below is an example of the syntax of the two systems.
GitHub Actions syntax for jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 5
jobs:
my_job:
steps:
- uses: actions/checkout@v4
- run: echo "Run my script here"
GitLab CI/CD syntax for jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 4-5
my_job:
variables:
GIT_CHECKOUT: "true"
script:
- echo "Run my script here"
Runners
-------
Runners are machines on which jobs are run. Both GitHub Actions and GitLab CI/CD
offer managed and self-hosted variants of runners. In GitHub Actions, the
``runs-on`` key is used to run jobs on different platforms, while in GitLab
CI/CD this is done with ``tags``.
GitHub Actions syntax for Runner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 2
my_job:
runs-on: ubuntu-latest
steps:
- run: echo "Hello Pythonistas!"
GitLab CI/CD syntax for Runner
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 3
my_job:
tags:
- linux
script:
- echo "Hello Pythonistas!"
Docker images
-------------
GitHub Actions syntax for Docker images
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 3
jobs:
my_job:
container: python:3.10
GitLab CI/CD syntax for Docker images
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 2
my_job:
image: python:3.10
.. seealso::
* `Run your CI/CD jobs in Docker containers
`_
Syntax for conditions and expressions
-------------------------------------
GitHub Actions uses the ``if`` keyword to prevent a job from running if a
condition is not met. GitLab CI/CD uses ``rules`` to determine whether a job is
executed under a certain condition.
Below is an example of the syntax of the two systems.
GitHub syntax for conditions and expressions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 3
jobs:
deploy:
if: contains( github.ref, 'main')
runs-on: ubuntu-latest
steps:
- run: echo "Deploy to production server"
GitLab syntax for conditions and expressions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
:emphasize-lines: 5-6
deploy:
stage: deploy
script:
- echo "Deploy to production server"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Besides ``if``, GitLab also offers other rules such as ``changes``, ``exists``,
``allow_failure``, ``variables`` and ``when``.
.. seealso::
* `rules `_
* `Specify when jobs run with rules
`_
Dependencies between jobs
-------------------------
Both GitHub Actions and GitLab CI/CD allow you to set dependencies for a job. In
both systems, jobs run in parallel by default, but GitLab CI/CD has a ``stages``
concept where jobs in one stage run concurrently, but the next stage does not
start until all jobs in the previous stage have completed. In GitHub Actions,
dependencies between jobs can be explicitly mapped with the ``needs`` key.
Below is an example of the syntax for each system. The workflows start with two
jobs running in parallel named ``unit-test`` and ``lint``. When these jobs are
completed, another job called ``deploy-to-stage`` is run. Finally, when
``deploy-to-stage`` is complete, the job ``deploy-to-prod`` is executed.
GitHub Actions syntax for dependencies between jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
jobs:
unit-test:
runs-on: ubuntu-latest
steps:
- run: echo "Running unit tests... This will take about 60 seconds."
- run: sleep 60
- run: echo "Code coverage is 0%"
lint:
runs-on: ubuntu-latest
steps:
- run: echo "Linting code... This will take about 10 seconds."
- run: sleep 10
- run: echo "No lint issues found."
deploy-to-stage:
runs-on: ubuntu-latest
needs: [unit-test,lint]
steps:
- run: echo "Deploying application in staging environment..."
- run: echo "Application successfully deployed to staging."
deploy-to-prod:
runs-on: ubuntu-latest
needs: [deploy-to-stage]
steps:
- run: echo "Deploying application in production environment..."
- run: echo "Application successfully deployed to production."
GitLab CI/CD syntax for dependencies between jobs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
stages:
- test
- stage
- prod
unit-test:
stage: test
script:
- echo "Running unit tests... This will take about 60 seconds."
- sleep 60
- echo "Code coverage is 0%"
lint:
stage: test
script:
- echo "Linting code... This will take about 10 seconds."
- sleep 10
- echo "No lint issues found."
deploy-to-stage:
stage: stage
script:
- echo "Deploying application in staging environment..."
- echo "Application successfully deployed to staging."
deploy-to-prod:
stage: prod
script:
- echo "Deploying application in production environment..."
- echo "Application successfully deployed to production."
Artefacts
---------
Both GitHub Actions and GitLab CI/CD can upload files and directories created by
a job as artefacts. These artefacts can be used to preserve data across multiple
jobs.
Below is an example of the syntax for both systems.
GitHub Actions syntax for artefacts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
- name: Archive code coverage results
uses: actions/upload-artifact@v3
with:
name: code-coverage-report
path: output/test/code-coverage.html
GitLab CI/CD syntax for artefacts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
script:
artifacts:
paths:
- output/test/code-coverage.html
Databases and service containers
--------------------------------
Both systems allow you to include additional containers for databases, caching
or other dependencies.
GitHub Actions uses the ``container`` key, while in GitLab CI/CD a container for
the job is specified with the ``image`` key. In both systems, additional service
containers are specified with the ``services`` key.
Below is an example of the syntax of the two systems.
GitHub Actions syntax for databases and service containers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Python
uses: actions/checkout@v4
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Test with pytest
run: python -m pytest
env:
DATABASE_URL: 'postgres://postgres:postgres@localhost:${{ job.services.postgres.ports[5432] }}/postgres'
GitLab CI/CD syntax for database and service containers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: yaml
test:
variables:
POSTGRES_PASSWORD: postgres
POSTGRES_HOST: postgres
POSTGRES_PORT: 5432
image: python:latest
services:
- postgres
script:
- python -m pytest
Mapping
-------
+-----------------------------------------------+-----------------------------------------------+
| GitHub | GitLab |
+===============================================+===============================================+
| **Concepts** |
+-----------------------------------------------+-----------------------------------------------+
| ``actions/upload-artifact@v2`` | ``artifacts`` |
+-----------------------------------------------+-----------------------------------------------+
| ``actions/cache@v4`` | ``cache`` |
+-----------------------------------------------+-----------------------------------------------+
| ``actions/download-artifact@v2`` | ``dependencies`` |
+-----------------------------------------------+-----------------------------------------------+
| ``environment`` | ``environment`` |
+-----------------------------------------------+-----------------------------------------------+
| ``container`` | ``image`` |
+-----------------------------------------------+-----------------------------------------------+
| ``actions/deploy-pages@main`` | ``pages`` |
+-----------------------------------------------+-----------------------------------------------+
| ``actions/create-release@v1`` | ``release`` |
+-----------------------------------------------+-----------------------------------------------+
| ``run`` | ``script``, ``after_script``, |
| | ``before_script``, ``trigger`` |
+-----------------------------------------------+-----------------------------------------------+
| ``hashicorp/vault-action@v2.5.0`` | ``secrets`` |
+-----------------------------------------------+-----------------------------------------------+
| ``services`` | ``services`` |
+-----------------------------------------------+-----------------------------------------------+
| ``runs-on`` | ``tags`` |
+-----------------------------------------------+-----------------------------------------------+
| ``timeout-minutes`` | ``timeout`` |
+-----------------------------------------------+-----------------------------------------------+
| **Environment variables** |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.api_url }}`` | ``CI_API_V4_URL`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.workspace }}`` | ``CI_BUILDS_DIR`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.ref }}`` | ``CI_COMMIT_BRANCH``, |
| | ``CI_COMMIT_REF_NAME``, |
| | ``CI_COMMIT_REF_SLUG``, |
| | ``CI_COMMIT_TAG``, |
| | ``CI_MERGE_REQUEST_REF_PATH`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.sha }}`` | ``CI_COMMIT_SHA``, |
| | ``CI_COMMIT_SHORT_SHA`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.job }}`` | ``CI_JOB_ID``, |
| | ``CI_JOB_NAME`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.event_name == | ``CI_JOB_MANUAL`` |
| 'workflow_dispatch' }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ job.status }}`` | ``CI_JOB_STATUS`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.server_url }}/${{ | ``CI_MERGE_REQUEST_PROJECT_URL`` |
| github.repository }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.token }}`` | ``CI_NODE_INDEX`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ strategy.job-total }}`` | ``CI_NODE_TOTAL`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.repository}}/${{ | ``CI_PIPELINE_ID`` |
| github.workflow }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.workflow }}`` | ``CI_PIPELINE_IID`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.event_name }}`` | ``CI_PIPELINE_SOURCE`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.actions }}`` | ``CI_PIPELINE_TRIGGERED`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.server_url }}/${{ | ``CI_PIPELINE_URL`` |
| github.repository }}/actions/runs/${{ | |
| github.run_id }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.workspace }}`` | ``CI_PROJECT_DIR`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.repository }}`` | ``CI_PROJECT_ID`` |
| | ``CI_PROJECT_PATH_SLUG``, |
| | ``CI_PROJECT_PATH``, |
| | ``CI_MERGE_REQUEST_PROJECT_ID``, |
| | ``CI_MERGE_REQUEST_PROJECT_PATH`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.event.repository.name | ``CI_PROJECT_NAME`` |
| }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.repository_owner }}`` | ``CI_PROJECT_NAMESPACE`` |
| | ``CI_PROJECT_ROOT_NAMESPACE`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_PROJECT_TITLE`` |
| github.event.repository.full_name }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.server_url }}/${{ | ``CI_PROJECT_URL`` |
| github.repository }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_REPOSITORY_URL`` |
| github.event.repository.clone_url }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ runner.os }}`` | ``CI_RUNNER_EXECUTABLE_ARCH`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.server_url }}`` | ``CI_SERVER_HOST``, ``CI_SERVER_URL`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.actions }}`` | ``CI_SERVER``, ``GITLAB_CI`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.actor }}`` | ``GITLAB_USER_EMAIL``, |
| | ``GITLAB_USER_ID``, |
| | ``GITLAB_USER_LOGIN``, |
| | ``GITLAB_USER_NAME`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ github.event_path }}`` | ``TRIGGER_PAYLOAD`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_ASSIGNEES`` |
| github.event.pull_request.assignees | |
| }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_ID``, |
| github.event.pull_request.number }}`` | ``CI_MERGE_REQUEST_IID`` |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_LABELS`` |
| github.event.pull_request.labels }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_MILESTONE`` |
| github.event.pull_request.milestone | |
| }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ |``CI_MERGE_REQUEST_SOURCE_BRANCH_NAME``, |
| github.event.pull_request.head.ref }}`` |``CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_NAME``|
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_SOURCE_BRANCH_SHA``, |
| github.event.pull_request.head.sha }}`` | ``CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_SHA``|
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_SOURCE_BRANCH_SHA``, |
| github.event.pull_request.head.repo.full_name | ``CI_MERGE_REQUEST_SOURCE_PROJECT_PATH`` |
| }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_SOURCE_PROJECT_URL`` |
| github.event.pull_request.head.repo.url }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ |``CI_MERGE_REQUEST_TARGET_BRANCH_NAME``, |
| github.event.pull_request.base.ref }}`` |``CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_NAME``|
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_TARGET_BRANCH_SHA``, |
| github.event.pull_request.base.sha }}`` | ``CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_SHA``|
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_MERGE_REQUEST_TITLE`` |
| github.event.pull_request.title }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_EXTERNAL_PULL_REQUEST_IID`` |
| github.event.pull_request.number }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ | ``CI_EXTERNAL_PULL_REQUEST_SOURCE_REPOSITORY``|
| github.event.pull_request.head.repo.full_name | |
| }}`` | |
+-----------------------------------------------+-----------------------------------------------+
| ``${{ |``RCI_EXTERNAL_PULL_REQUEST_TARGET_REPOSITORY``|
| github.event.pull_request.base.repo.full_name | |
| }}`` | |
+-----------------------------------------------+-----------------------------------------------+