Leaving GitHub: why I'm enjoying GitLab and CI/CD components
I moved my projects from GitHub to GitLab this year. I expected to miss GitHub. Instead, a few GitLab features ended up shaping how I organise everything, and CI/CD components are the one I'd find hardest to give up.
Why I moved
At some point I realized most of my projects are configured almost the same way. Every UI project deploys to Cloudflare with the wrangler CLI. Every backend service builds an image and uses kubectl to roll out a deployment on my cluster. And every project (Node or Go) runs the same steps to lint, compile and test.
So whenever I created a new project, I found myself copy-pasting entire workflow YAMLs from another repo. And whenever I wanted to change the pipeline, I had to go repo by repo and make the same edit again and again.
Then I learned about GitLab CI/CD components, which let me pull all of that shared logic out into one place, and I fell in love. Now, if I want to change anything in my pipelines, I create a new version of the component and bump the version in each project's pipeline. That's it.
CI/CD components
This is the part I'm enjoying most. A CI/CD component is a versioned, reusable piece of pipeline with typed inputs, published from a repository. I keep mine in bolanosdev_corp/platform/gitlab, one project per kind of app: Go services, nginx images, Cloudflare Workers. A project's whole pipeline becomes a few includes. Here's a UI project:
# A frontend: deploy to Cloudflare with wrangler. include: - component: gitlab.com/bolanosdev_corp/platform/gitlab/components-workers/[email protected] inputs: staging-url: https://staging.brawney.com production-url: https://brawney.com stages: [deploy]
And a backend service:
# A Go service: test, push the image, roll it out on the cluster. include: - component: gitlab.com/bolanosdev_corp/platform/gitlab/components-go/[email protected] - component: gitlab.com/bolanosdev_corp/platform/gitlab/components-go/[email protected] inputs: job-name: push:main branch: main image: bd-auth-service tag: latest - component: gitlab.com/bolanosdev_corp/platform/gitlab/components-go/[email protected] inputs: job-name: deploy:main branch: main namespace: bolanosdev-prod deployment: bd-auth-service-deployment needs: ["push:main"] stages: [test, build, deploy]
These aren't just examples. A couple of public projects use components today, so you can see the real thing:
- Bennie-UI's Storybook: its .gitlab-ci.yml builds on the shared Bun setup from
components-nodeto test and deploy Storybook (pipelines). - bennie-ui.com: its .gitlab-ci.yml includes the
components-nodetest job and deploys the site to Cloudflare Workers (pipelines). - The components themselves live in bolanosdev_corp/platform/gitlab: components-node, components-workers and components-go, each with a README showing how to include it.
What makes it work in practice:
- Versions. Each repo pins a tag (
@v0.3.2). I can change a component, tag it, and move repos over one at a time instead of breaking all of them at once. - Inputs. The test path, the project name, the cluster: everything that differs between repos is an input with a default, so the component itself stays generic.
- Jobs stay yours. A job added by a component can still be extended in the repo's own
.gitlab-ci.yml, so one-off needs don't fork the component:
# The component deploys main; this repo also deploys staging,
# with its own URLs baked into the build.
cloudflare-pages-deploy:
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_COMMIT_BRANCH == "staging"'
variables:
VITE_AUTH_URL: https://auth-staging.bolanos.dev
VITE_API_URL: https://api-staging.bolanos.devDay to day
The rest is small things that add up. Merge requests start as drafts. Labels gate the expensive pipelines. The glab CLI covers merge requests, pipelines, job logs and the API from the terminal. Most of my GitLab time happens without opening a browser.
If you have more than a few related repositories and you're tired of keeping their pipelines in sync, try components. Start with the job you copy most often.