Actions to CI/CD
Translating GitHub Actions workflows to GitLab CI/CD.
GitHub Actions workflows don't import. You rebuild them as .gitlab-ci.yml.
The scripts, build commands, and Docker images mostly stay as-is — most of
the work is translating YAML. AI-Assisted Migration
walks through prompt patterns that handle this conversion well.
Concept mapping
| GitHub Actions | GitLab CI/CD |
|---|---|
.github/workflows/*.yml |
.gitlab-ci.yml |
| Workflow | Pipeline |
| Job | Job |
| Step (within a job) | Line in script: |
| Workflow event triggers | rules: / workflow: |
| Actions Marketplace | CI/CD Components |
| Secrets | CI/CD Variables (Settings → CI/CD → Variables) |
Stages run sequentially; jobs in a stage run in parallel. needs: overrides
stage order for explicit DAGs.
Syntax conversion
| Concept | GitHub Actions | GitLab CI/CD |
|---|---|---|
| Checkout | uses: actions/checkout@v4 |
Automatic (control with GIT_STRATEGY) |
| Environment variable | ${{ env.VAR }} |
$VAR or ${VAR} |
| Conditional | if: github.ref == 'refs/heads/main' |
rules: - if: $CI_COMMIT_BRANCH == "main" |
| Artifact | actions/upload-artifact@v4 |
artifacts: { paths: [dist/] } |
| Cache | actions/cache@v4 |
cache: { key, paths } |
| Job dependency | needs: [build] |
needs: [build] |
| Container | container: node:18 |
image: node:18 |
| Matrix | strategy.matrix |
parallel.matrix |
Migration steps
- Inventory. List workflows in
.github/workflows/. Convert one at a time. - Create
.gitlab-ci.ymlat the repository root. - Define stages matching the workflow's job graph.
- Translate jobs. Each job becomes a top-level YAML key with
stage,image, andscript. - Move secrets. Recreate GitHub Secrets as CI/CD Variables under Settings → CI/CD → Variables. Mark sensitive values Masked and (where possible) Protected.
- Validate. Use
your-gitlab-instance/-/ci/lintor the CI/CD → Editor. - Run. Commit, watch the first pipeline, fix what breaks.
Examples
Build and test
# GitHub Actions
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '18' }
- run: npm install
- run: npm test
- run: npm run build
# .gitlab-ci.yml
stages: [test, build]
test:
stage: test
image: node:18
script:
- npm install
- npm test
build:
stage: build
image: node:18
script:
- npm install
- npm run build
artifacts:
paths: [dist/]
Matrix build
# GitHub Actions
jobs:
test:
strategy:
matrix:
node-version: [16, 18, 20]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: ${{ matrix.node-version }} }
- run: npm test
# .gitlab-ci.yml
test:
parallel:
matrix:
- NODE_VERSION: ['16', '18', '20']
image: node:${NODE_VERSION}
script:
- npm install
- npm test
Conditional deploy
# GitHub Actions
deploy:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
# .gitlab-ci.yml
deploy:
stage: deploy
script: ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == "main"
Docker build and push (GitLab container registry)
docker:
stage: build
image: docker:latest
services: [docker:dind]
script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" .
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
Common Action equivalents
| GitHub Action | GitLab equivalent |
|---|---|
actions/checkout@v4 |
Built-in |
actions/setup-node@v4 |
image: node:18 |
actions/setup-python@v5 |
image: python:3.11 |
actions/cache@v4 |
cache: keyword |
actions/upload-artifact@v4 |
artifacts: keyword |
actions/download-artifact@v4 |
dependencies: or needs: |
docker/build-push-action@v5 |
Docker commands with the docker:dind service |
Useful built-in variables
$CI_COMMIT_BRANCH Current branch
$CI_COMMIT_SHA Commit SHA
$CI_PROJECT_NAME Project name
$CI_PROJECT_DIR Working directory
$CI_REGISTRY GitLab container registry URL
$CI_REGISTRY_IMAGE Full image path for this project
$CI_PIPELINE_ID Pipeline ID
$CI_JOB_NAME Current job name
Full reference: Predefined variables.
Pitfalls
Common pitfalls
- Recreate GitHub Secrets as CI/CD Variables before running pipelines. A missing variable surfaces as a runtime failure, not a config error.
- Not every Action has a direct equivalent. Bespoke shell or a CI/CD Component is usually cleaner than chasing a one-to-one mapping.
- Required status checks aren't imported. Recreate them as merge request approval rules.