ProbableOdyssey | Blake Cook

Migrating to GitLab

· 5 min read · 901 words

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?

  1. Create a personal access token on GitHub

  2. Create an account on GitLab

  3. 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 glab

Then 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 test

And here’s the GitLab equivalent:

# .gitlab-ci.yml
stages:
  - test

test:
  stage: test
  image: node:20
  script:
    - npm ci
    - npm test
  only:
    - push

For 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 test

An 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:

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.