By admin , 29 April 2026

The CI checkout

CI environments check out your code afresh on every job. The choices you make about how they check out determine cache hits, build speed, and security. The defaults are rarely optimal.

Shallow clone for speed

git clone --depth=1 https://example.com/repo.git
# GitHub Actions:
- uses: actions/checkout@v4
  with:
    fetch-depth: 1

A depth-1 clone fetches only the tip commit - sufficient for build-and-test on modest repos. For tools that need history (versioning, blame, changelogs), unshallow on demand:

git fetch --unshallow

Sparse checkout for monorepos

git sparse-checkout init --cone
git sparse-checkout set apps/web shared/ui

Combine with shallow clone: only the directories you need, only the latest commit.

Caching .git across jobs

The .git directory is the most cacheable artefact in a pipeline. Most CI systems support persistent workspaces or runner caches:

cache:
  key: git-$CI_COMMIT_REF_SLUG
  paths:
    - .git/

Authenticated cloning

Use HTTPS with a token, not stored SSH keys, in ephemeral runners. GitHub provides GITHUB_TOKEN; GitLab provides CI_JOB_TOKEN. Never bake long-lived credentials into images.

git clone https://oauth2:${TOKEN}@gitlab.com/group/repo.git

Reproducible builds

Pin everything by SHA, not branch. Lockfiles (package-lock.json, poetry.lock, Cargo.lock) belong in Git. CI should fail if the lockfile drifts:

npm ci          # fails if lockfile is out of date
cargo build --locked

Detecting changed files

Run only relevant jobs by diffing against the merge base:

BASE=$(git merge-base origin/main HEAD)
CHANGED=$(git diff --name-only $BASE)
if echo "$CHANGED" | grep -q '^apps/web/'; then
  ./scripts/test-web.sh
fi

Tag-driven workflows

Separate "build on every push" from "publish on tag". Branch pipelines run tests; tag pipelines build, sign, and publish. This keeps responsibility clean.

Secrets out of Git

Never commit secrets to be "decoded by CI". Use the host's secret store. Run gitleaks in CI to fail builds containing leaked secrets:

- uses: gitleaks/gitleaks-action@v2

Tagging the build

Bake the commit SHA and build number into your artefacts:

SHA=$(git rev-parse --short HEAD)
docker build -t myapp:$SHA -t myapp:latest .

This makes "what is running in production?" answerable with docker inspect.

The checklist

  • Shallow clone unless you need history.
  • Cache .git and language caches.
  • Use ephemeral, scoped tokens.
  • Run secret scanning in CI, not just on commit.
  • Tag artefacts with the SHA they were built from.