A place I was at recently used makefiles in each project containing entries for "build", "deploy", "test" etc. All the CI did was use shared generic jobs to call the relevant makefile command.
These could just as easily be run from a developers machine with the right credentials.
These could just as easily be run from a developers machine with the right credentials.