Migrating to GitLab
Table of Contents
GitHub’s reliability has gone down the drain. What used to be a rock-solid platform now feels uncomfortably fragile. During this period at my last workplace, incidents would grind our pipelines to a total halt, PRs couldn’t be merged, and some reviews were even lost!
Many predicted this after the Microsoft acquisition. This downtime is incredibly costly for teams that rely on GitHub for daily work. Momentum is lost, and trust is eroded.
If I couldn’t use GitHub anymore in the worst-case scenario, what would I do?
This really shouldn’t be an issue. After all, GitHub is just a wrapper around git — a distributed version control system. The lock-in of the platform is the issue. Network effects result in GitHub being the main place for a developers portfolio. But I set that aside and found that GitLab is still well-equipped to provide a stable platform that mirrors content to GitHub (or several other platforms)!
So let’s swap over to GitLab, what steps do we need?
Create a personal access token on GitHub
Create an account on GitLab
Follow the dialogues and use the GitHub token to import repositories
Done!
One caveat with the one-click repo migration: sometimes the repo name might be slightly different when the repo names contain
.or-characters, but this can be fixed in the GitLab repo settings afterwards if necessary.
Now all I need to do is update the origin remote for all my local repos. I
prefer using CLI interfaces, and to my surprise the GitLab CLI is as impressive
as the GitHub CLI!
brew install gh glabThen some “nice” git aliases for these do I can use git hub <command> and
git lab <command>:
# ~/.gitconfig
[alias]
hub = "!NO_COLOR=true gh"
lab = "!NO_COLOR=true glab"Using the GitLab CLI, I wrote this git alias to update the remote on a local repo from GitHub to GitLab:
# ~/.gitconfig
[alias]
lab-remote = "!f() { \
repo_guess=$(git remote get-url origin | sed 's/.*://' | sed \"s|.*/|$GITLAB_USER/|\" | sed 's/.git$//'); \
echo "try switching to ${repo_guess}" ; \
if git lab repo view $repo_guess; then \
git remote set-url origin git@gitlab.com:${repo_guess}; \
git remote -v; \
fi; \
}; f"Once I’ve exported GITHUB_USER=... and GITLAB_USER=... in my .bashrc, I
just need to call git lab-remote in my local repo to update it!
(alternatively I could’ve just deleted my local repos and re-cloned them from the new GitLab repo address — but why spend 5 mins doing something manually when you can spend 20 mins scripting it?)
To maintain my presence on GitHub as the “public” interface, each repo allows
you to use your GitHub token to mirror content from GitLab to GitHub! This can
be set up through the web UI, but of course I’ve also written a git alias for
this as well. This one requires exporting the token as GITLAB_MIRROR=..., but
the basic gist of it is:
# ~/.gitconfig
[alias]
lab-mirror = "!f() { \
repo=$(git remote get-url origin | sed 's/.*://' | sed \"s/$GITLAB_USER/$GITHUB_USER/\") ; \
key=${GITLAB_MIRROR} ; \
if [ -z ${key} ]; then \
echo No key found ; \
exit 1; \
fi; \
git lab repo mirror --direction push --url https://${GITHUB_USER}:${key}@github.com/$repo; \
}; f"And that’s it. With the mirroring in place, you get the best of both worlds: GitLab as your stable daily driver and GitHub as the public face for your portfolio and external contributions.
When you push to GitLab, changes automatically flow to GitHub. Your collaborators, CI pipelines, and review processes stay on the reliable platform, while the GitHub world sees your projects as active as ever.
But What about CI? GitLab has you covered as well! (Though keep in mind GitLab offers 400 free minutes whereas GitHub offers 2000)
GitHub Actions uses a flat YAML file with on: triggers and a jobs: section.
GitLab CI uses .gitlab-ci.yml at the root of your repo with stages: and
individual jobs.
Here’s a typical GitHub Actions workflow:
# .github/workflows/main.yml
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
- run: npm testAnd here’s the GitLab equivalent:
# .gitlab-ci.yml
stages:
- test
test:
stage: test
image: node:20
script:
- npm ci
- npm test
only:
- pushFor a single-job pipeline, the migration is almost a direct translation:
# .gitlab-ci.yml
default:
image: node:20
stages:
- test
test:
stage: test
script:
- npm ci
- npm testAn observation: GitHub leans heavily on reusing steps across projects through a “marketplace” of actions, whereas GitLab forces writing explicit scripts. This is a massive advantage in my eyes. GitHub actions are a common attack vector for vulnerabilities that have been exploited in the latest high-profile hacks. Auditing GitHub actions is a bit of a goose chase!
In GitHub Actions, you’d set secrets in the repo settings under
Settings > Secrets and variables. In GitLab, it’s
Settings > CI/CD > Variables. The syntax for using them is slightly
different:
- GitHub:
${{ secrets.MY_SECRET }} - GitLab:
$MY_SECRET(or$SECRETfor protected variables)
Once you’ve tested the GitLab pipeline, you can delete the .github/ directory
from your repo — GitLab runs the show now!
More complex CI/CD pipelines are out of my scope for now, but GitLab offers
All considered, this process took me about 3 hours across most of my projects. I’d highly encourage anyone to follow this path for personal projects and small teams!
#platform-reliability #ci/cd #github #version-control #repository-migration #migration #ci #devops #reliability #git #gitlab
Reply to this post by email blZake@proZbableodyssey.blog (remove Z characters) ↪
Comments
Leave a comment
Markdown is supported. Your email is private and only used if you'd like a reply.