CI for Databases on GitLab
GitLab is a popular, open-source alternative to GitHub. In addition to a self-hosted version, GitLab also offers a hosted version at gitlab.com. Similar to GitHub, GitLab offers users storage for Git repositories, issue tracking, and CI/CD pipelines.
In this guide we will demonstrate how to use GitLab CI and Atlas to setup CI pipelines for your database schema changes.
Prerequisites
- A GitLab project with an Atlas migration directory.
Setup
Install Atlas
- macOS + Linux
- Homebrew
- Docker
- Windows
- Manual Installation
To download and install the latest release of the Atlas CLI, simply run the following in your terminal:
curl -sSf https://atlasgo.sh | sh
Get the latest release with Homebrew:
brew install ariga/tap/atlas
To pull the Atlas image and run it as a Docker container:
docker pull arigaio/atlas
docker run --rm arigaio/atlas --help
If the container needs access to the host network or a local directory, use the --net=host
flag and mount the desired
directory:
docker run --rm --net=host \
-v $(pwd)/migrations:/migrations \
arigaio/atlas migrate apply
--url "mysql://root:pass@:3306/test"
Download the latest release and move the atlas binary to a file location on your system PATH.
Login to Atlas
After installing Atlas locally, you will need to login to your Atlas account. If you don't have an account yet, you will be prompted to create one for free during the login process.
You can do this by running the following command:
atlas login
Push your migrations to Atlas
Once you have logged in, you can push your migrations to Atlas by running the following command:
- PostgreSQL
- MySQL
- MariaDB
- SQLite
- SQL Server
- ClickHouse
- Redshift
atlas migrate push app \
--dev-url "docker://postgres/15/dev?search_path=public"
atlas migrate push app \
--dev-url "docker://mysql/8/dev"
atlas migrate push app \
--dev-url "docker://mariadb/latest/dev"
atlas migrate push app \
--dev-url "sqlite://dev?mode=memory"
atlas migrate push app \
--dev-url "docker://sqlserver/2022-latest"
atlas migrate push app \
--dev-url "docker://clickhouse/23.11"
atlas migrate push app \
--dev-url "redshift://user:pass@redshift-cluster:5439/dev"
Atlas will print a URL leading to your migrations on Atlas Cloud. You can visit this URL to view your migrations.
Create a Bot Token
To connect GitLab CI to Atlas, you will need to create a bot token. To learn how to create a bot token, please visit the Bot Tokens guide.
Setup GitLab CI
With your migrations pushed to Atlas, you can now setup GitLab CI to run your migrations on every commit.
Create a variable for your bot token
In order to connect GitLab CI to Atlas, you will need to create a variable for your bot token. To do this:
- Go to your projects Settings page.
- Click on the CI/CD tab.
- Expand the Variables tab.
- Click "Add Variable".
- Use
ATLAS_CLOUD_TOKEN
as the variable key and paste the bot token you created earlier as the variable value. - Set the "Mask Variable" checkbox to true to prevent the token from being printed in the CI logs.
- Unset the "Protect Variable" checkbox so that this job can be run on all branches (necessary for running on merge requests).
Create a .gitlab-ci.yml
file
Next, you will need to create a .gitlab-ci.yml
file in the root of your repository. This file will tell GitLab CI
how to run your Atlas CI pipeline.
- SQLite
- PostgreSQL
- MySQL
image: ubuntu:latest
stages:
- lint
- push
before_script:
- apt-get update -q && apt-get install -y curl
- 'which atlas || (curl -sSf https://atlasgo.sh | sh)'
- atlas login --token $ATLAS_CLOUD_TOKEN
lint migrations:
stage: lint
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: always
- if: '$CI_MERGE_REQUEST_ID'
changes:
- migrations/*
when: always
allow_failure: false
script:
- |
if [ -n "$CI_MERGE_REQUEST_IID" ]; then
URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
else
URL="${CI_PROJECT_URL}/-/commit/${CI_COMMIT_SHA}"
fi
ATLAS_CONTEXT=$(cat <<EOF
{
"repo": "$CI_PROJECT_PATH",
"branch": "$CI_COMMIT_REF_NAME",
"commit": "$CI_COMMIT_SHA",
"url": "$URL"
}
EOF
)
- atlas migrate lint --base atlas://gitlab -w --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"
push migrations:
stage: push
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
script:
- |
ATLAS_CONTEXT=$(cat <<EOF
{
"repo": "$CI_PROJECT_PATH",
"branch": "$CI_COMMIT_REF_NAME",
"commit": "$CI_COMMIT_SHA",
"url": "${CI_PROJECT_URL}"
}
EOF
)
- atlas migrate push gitlab --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"
image: ubuntu:latest
services:
- postgres
variables:
POSTGRES_DB: db
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
stages:
- lint
- push
before_script:
- apt-get update -q && apt-get install -y curl
- 'which atlas || (curl -sSf https://atlasgo.sh | sh)'
- atlas login --token $ATLAS_CLOUD_TOKEN
lint migrations:
stage: lint
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: always
- if: '$CI_MERGE_REQUEST_ID'
changes:
- migrations/*
when: always
allow_failure: false
script:
- |
if [ -n "$CI_MERGE_REQUEST_IID" ]; then
URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
else
URL="${CI_PROJECT_URL}/-/commit/${CI_COMMIT_SHA}"
fi
ATLAS_CONTEXT=$(cat <<EOF
{
"repo": "$CI_PROJECT_PATH",
"branch": "$CI_COMMIT_REF_NAME",
"commit": "$CI_COMMIT_SHA",
"url": "$URL"
}
EOF
)
- atlas migrate lint --base atlas://gitlab -w --dev-url "postgres://user:pass@postgres/db?sslmode=disable" --context "$ATLAS_CONTEXT"
push migrations:
stage: push
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
script:
- |
ATLAS_CONTEXT=$(cat <<EOF
{
"repo": "$CI_PROJECT_PATH",
"branch": "$CI_COMMIT_REF_NAME",
"commit": "$CI_COMMIT_SHA",
"url": "${CI_PROJECT_URL}"
}
EOF
)
- atlas migrate push gitlab --dev-url "postgres://user:pass@postgres/db?sslmode=disable" --context "$ATLAS_CONTEXT"
image: ubuntu:latest
services:
- mysql
variables:
MYSQL_DATABASE: db
MYSQL_ROOT_PASSWORD: pass
stages:
- lint
- push
before_script:
- apt-get update -q && apt-get install -y curl
- 'which atlas || (curl -sSf https://atlasgo.sh | sh)'
- atlas login --token $ATLAS_CLOUD_TOKEN
lint migrations:
stage: lint
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: always
- if: '$CI_MERGE_REQUEST_ID'
changes:
- migrations/*
when: always
allow_failure: false
script:
- |
if [ -n "$CI_MERGE_REQUEST_IID" ]; then
URL="${CI_PROJECT_URL}/-/merge_requests/${CI_MERGE_REQUEST_IID}"
else
URL="${CI_PROJECT_URL}/-/commit/${CI_COMMIT_SHA}"
fi
ATLAS_CONTEXT=$(cat <<EOF
{
"repo": "$CI_PROJECT_PATH",
"branch": "$CI_COMMIT_REF_NAME",
"commit": "$CI_COMMIT_SHA",
"url": "$URL"
}
EOF
)
- atlas migrate lint --base atlas://gitlab -w --dev-url "mysql://root:pass@mysql/db" --context "$ATLAS_CONTEXT"
push migrations:
stage: push
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
script:
- |
ATLAS_CONTEXT=$(cat <<EOF
{
"repo": "$CI_PROJECT_PATH",
"branch": "$CI_COMMIT_REF_NAME",
"commit": "$CI_COMMIT_SHA",
"url": "${CI_PROJECT_URL}"
}
EOF
)
- atlas migrate push gitlab --dev-url "mysql://root:pass@mysql/db" --context "$ATLAS_CONTEXT"
Let's break down what this file is doing:
- The
image
directive tells GitLab CI to use theubuntu:latest
Docker image to run your CI jobs. - The
stages
directive defines the stages that your CI pipeline will run. In this case, we have two stages:lint
andpush
. In thelint
stage, we will run theatlas migrate lint
command to verify that our migrations are valid and safe. In thepush
stage, we will run theatlas migrate push
command to push our migrations to Atlas. - The
before_script
directive defines a set of commands that will be run before each job. In this case, we are installing theatlas
CLI and logging into Atlas using the bot token we created earlier. - The
lint migrations
job will run theatlas migrate lint
command. This job will run on all commits to your default branch and on all merge requests. This job will fail if the migrations are invalid. - The
push migrations
job will run theatlas migrate push
command. This job will only run on commits to your default branch.
Commit and push your changes
Now that you have created your .gitlab-ci.yml
file, you can commit and push your changes to GitLab.
From this point on, whenever you make a change to your migrations and push them to GitLab, GitLab CI will run your Atlas CI pipeline to verify their safety. To view a detailed analysis of your schema changes, look at the logs for a direct link to your Atlas Cloud account:
$ which atlas || (curl -sSf https://atlasgo.sh | sh)
Downloading https://release.ariga.io/atlas/atlas-linux-amd64-latest
Installation successful!
$ atlas login --token $ATLAS_CLOUD_TOKEN
You are now connected to "rotemtam85" on Atlas Cloud.
$ atlas migrate lint --base atlas://gitlab -w --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"
https://rotemtam85.atlasgo.cloud/ci-runs/8589934616
Cleaning up project directory and file based variables
Once you merge your changes to your default branch, the pipeline will push your latest changes to Atlas, so they can be deployed. Similarly, to get a visual representation of your database schema, follow the link from the logs to Atlas Cloud:
$ which atlas || (curl -sSf https://atlasgo.sh | sh)
Downloading https://release.ariga.io/atlas/atlas-linux-amd64-latest
Installation successful!
$ atlas login --token $ATLAS_CLOUD_TOKEN
You are now connected to "rotemtam85" on Atlas Cloud.
$ atlas migrate push gitlab --dev-url "sqlite://demo?mode=memory" --context "$ATLAS_CONTEXT"
https://rotemtam85.atlasgo.cloud/dirs/4294967302
Next Steps: Deployments
Congratulations! You have successfully setup GitLab CI to lint and push your migration directory to Atlas Cloud. The next step is to deploy your migrations to your production database directly from Atlas Cloud using the CLI.