Rationale
You want to keep your project healthy, approachable, and maintain readable CI/CD pipelines. You want to avoid strong dependencies on specific vendors, technologies, or CI runners, ensuring the flexibility to switch providers if needed.
Vendor lock-in is problematic, among other reasons, because:
- Changes in the cost model
- Changes in the ToS
- Feature sets, and the corresponding API surface may change and go in a direction you don’t like
- Competitors may get better and may be re-considered, new options can appear
How
With a few simple precautions and sustained attention, you can achieve this:
-
Where possible, implement CI/CD steps in a “native” way (i.e. commands runnable by any shell), with your platform CI/CD (e.g. GitHub Actions, GitLab pipelines, etc) functioning solely as an executor.
Example
The docker-build.yaml file in isso-comments/isso shows GitHub Action-specific “jargon” and heavy reliance on external actions. In contrast, rgbds build-container.yml accomplishes similar tasks minimizing reliance on GitHub Actions specifics, prioritizing native scripting/commands.
-
Don’t rely on platform-specific features in the YAML, unless strictly necessary (e.g. for triggers). When doing so, comment and document appropriately.
-
Limit manual steps. If a manual step is involved, document it directly in the pipelines or in a separate “CICD.md” document.
-
All scripts should be runnable locally with minimal changes, allowing contributors to reproduce, debug, and test workflows outside the CI environment. If possible, comment directly on the YAML suggesting how platform-specific steps can be run locally.
-
Choose the appropriate level of abstraction in your workflows. Favor simple scripts or commands when possible, rather than relying on complex or third-party platform-specific actions. Keeping things straightforward improves readability and maintainability.
When to use higher-level abstractions
For platform-specific features (e.g. deploying to GitHub Pages) it often makes sense to adopt well-maintained, documented actions that encapsulate those platform details. For example, in this commit, we replaced manual steps like
git pull
,commit
, andpush
with a single “publish to GitHub Pages” action. In these cases, higher-level tools simplify inherently platform-specific processes and reduce the risk of inconsistencies. -
Don’t be scared of over-documenting. Setting up a CI/CD, even if it already exists, can be a daunting task and something can always be missed somewhere. It also cannot be fully automated because of the moving (and not exposed) surfaces of the vendors/platforms. Document.
-
Every once in a while (set up a policy for this), try to see how easy would it be to re-set up everything from the ground up and how much would it cost. Is there something you wouldn’t know how to do? Or only some person would know? Document.
Idea
This could be a nice first task for newcomers. Only give them a link to the main docs and see if they manage. Adjust accordingly after their feedback.
-
Pay attention to the steps and assets that are not (and cannot be) part of the git repository itself. This includes flags and settings in the CI platform, tokens (how do you generate AND set them), repository secrets, ssh keys to perform git operations in the pipelines, etc. If your platform exposes an endpoint to export, in a serialized version, the entire repository settings, perform that regularly (even if the same file cannot be “repushed” into the platform to re-create the repository, it’s a good reference). Document.
-
Do not use CI/CD pipelines for things that should go somewhere else. CI pipelines are used to automate testing, building, and deployment processes. Don’t risk side-effects by adding one-off jobs as manual triggered pipelines.
-
When writing templates, think about the ergonomics in using such templated action and reflect on the use case you’re trying to cover. Make sure you’re not hiding away (or leaving as un-accessible) necessary parameters passable to the underlying basic tools.
Example
If you’re templating a container build + push to a registry action, make sure
--build-arg
s are still passable from outside.
Thanks to Davide and Glenn for the precious feedback, proofreadings and contributions.