This is one of the advantages Gitlab has because the ability to do this is built into gitlab-runner so you don’t have to maintain anything, you’re using the same code Gitlab itself is.
Any use of rules, cache, includes, or ... you know, real life ... gitlab-ci constructs makes `gitlab-runner exec my-job` do absolutely nothing helpful. The circleci binary did as advertised on the tin and has been my favorite "debug ci builds locally" experience